package main

import (
	"AOC2021/src/helper"
	"fmt"
	"os"
	"strings"
)

type snailNumber struct {
	pair   [2]*snailNumber
	val    int
	nested int
}

func main() {
	args := os.Args[1:]
	input, err := helper.GetInput(args[0])
	if err != nil {
		fmt.Println(err)
	}
	var snailNumberArray []snailNumber
	for _, row := range input {
		number, _ := parseSnailNumber(row[1:], 0)
		snailNumberArray = append(snailNumberArray, number)
	}
	fmt.Println(part1(input))

	max := 0
	for i, _ := range input {
		for j, _ := range input {
			if i != j {
				tmpResult := part1([]string{input[i], input[j]})
				if max < tmpResult {
					max = tmpResult
				}
			}
		}
	}
	fmt.Println(max)
}

func part1(input []string) int {
	currentNumber := replaceBracketsWithParenthesis(input[0])
	for i := 1; i < len(input); i++ {
		currentNumber = addSnailNumberStrings(currentNumber, replaceBracketsWithParenthesis(input[i]))
		currentNumber = doAll(currentNumber)
	}
	reduce := true
	testString := magicMagic(currentNumber)
	for reduce {
		testString, reduce = reduceNumber(testString)
	}
	return testString[0]
}

func reduceNumber(snailNumber []int) ([]int, bool) {
	index := 0
	for index < len(snailNumber)-2 {
		if snailNumber[index] > -1 && snailNumber[index+2] > -1 {
			tmpIntLeft := snailNumber[index]
			tmpIntRight := snailNumber[index+2]
			tmpInt := 3*tmpIntLeft + 2*tmpIntRight
			returnArray := append(snailNumber[:index], snailNumber[index+4:]...)
			returnArray[index-1] = tmpInt
			return returnArray, true
		}
		index++
	}
	return snailNumber, false
}

func magicMagic(number string) []int {
	returnArray := []int{}
	for _, char := range number {
		switch char {
		case ',':
			returnArray = append(returnArray, -2)
		case '(':
			returnArray = append(returnArray, -1)
		case ')':
			returnArray = append(returnArray, -3)
		default:
			returnArray = append(returnArray, int(char-'0'))
		}
	}
	return returnArray
}

func replaceBracketsWithParenthesis(input string) string {
	input = strings.Replace(input, "]", ")", -1)
	input = strings.Replace(input, "[", "(", -1)
	return input
}

func doAll(currentNumber string) string {
	run := true
	for run {
		exploded := true
		splitted := false
		for exploded {
			currentNumber, exploded = checkExplode(currentNumber)
		}
		currentNumber, splitted = checkSplit(currentNumber)
		run = splitted || exploded
	}
	return currentNumber
}

func addSnailNumberStrings(left string, right string) string {
	return "(" + left + "," + right + ")"
}

func checkExplode(snailNumberString string) (string, bool) {
	index := 0
	openBracketCounter := 0
	for index < len(snailNumberString) {
		char := snailNumberString[index]
		index++
		if char == '(' {
			openBracketCounter++
		}
		if char == ')' {
			openBracketCounter--
		}
		if runeIsNumber(rune(char)) && openBracketCounter >= 5 {
			return explode(snailNumberString, index), true
		}
	}
	return snailNumberString, false
}

func checkSplit(snailNumberString string) (string, bool) {
	index := 0
	for index < len(snailNumberString) {
		if runeIsNumberOver9(rune(snailNumberString[index])) {
			tmpInt := snailNumberString[index] - '0'
			insertNewPair(tmpInt, &snailNumberString, index)
			return snailNumberString, true
		}
		index++
	}
	return snailNumberString, false
}

func explode(snailNumberString string, location int) string {
	returnString := snailNumberString[:location-2] + "0" + snailNumberString[location+3:]
	addedLeft := false
	index := location - 3
	for index > 0 && !addedLeft {
		if runeIsNumber(rune(returnString[index])) {
			tmpRuneArray := []rune(returnString)
			tmpInt := snailNumberString[location-1] - '0' + returnString[index] - '0'
			tmpRuneArray[index] = rune(tmpInt + '0')
			returnString = string(tmpRuneArray)
			index = location
			addedLeft = true
		}
		index--
	}
	if index == 0 {
		index = location - 1
	}
	addedRight := false
	for !addedRight && index < len(returnString) {
		if runeIsNumber(rune(returnString[index])) {
			tmpRuneArray := []rune(returnString)
			tmpInt := snailNumberString[location+1] - '0' + returnString[index] - '0'
			tmpRuneArray[index] = rune(tmpInt + '0')
			returnString = string(tmpRuneArray)
			addedRight = true
		}
		index++
	}
	return returnString
}

func insertNewPair(tmpInt uint8, returnString *string, index int) {
	tmpNumberLeft := tmpInt / 2
	tmpNumberRight := tmpNumberLeft + (tmpInt % 2)
	tmpNewPairString := string([]rune{'(', rune(tmpNumberLeft) + '0', ',', rune(tmpNumberRight) + '0', ')'})
	*returnString = (*returnString)[:index] + tmpNewPairString + (*returnString)[index+1:]
}

func parseSnailNumber(input string, nested int) (number snailNumber, index int) {
	index = 0
	number.nested = nested
	currentPairToFill := &number.pair[0]
	for index < len(input) {
		char := input[index]
		index++
		if char == '[' {
			tmpNumber, tmpIndex := parseSnailNumber(input[index:], nested+1)
			index += tmpIndex
			*currentPairToFill = &tmpNumber
		}
		if char == ',' {
			currentPairToFill = &number.pair[1]
		}
		if runeIsNumber(rune(char)) {
			(*currentPairToFill) = &snailNumber{[2]*snailNumber{}, int(char - '0'), nested + 1}
		}
		if char == ']' {
			return
		}
	}
	return
}

func runeIsNumber(char rune) bool {
	return char != 44 && char != 40 && char != 41
}

func runeIsNumberOver9(char rune) bool {
	return char != 44 && char != 40 && char != 41 && char > 57
}
