package main

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

func main() {
	args := os.Args[1:]
	input, err := helper.GetInput(args[0])
	if err != nil {
		fmt.Println(err)
	}
	commandsSplitAtInput := [][]string{}
	for _, command := range input {
		if helper.ContainsCharacter(command, "inp") {
			commandsSplitAtInput = append(commandsSplitAtInput, []string{})
		}
		commandsSplitAtInput[len(commandsSplitAtInput)-1] = append(commandsSplitAtInput[len(commandsSplitAtInput)-1], command)
	}
	vars := [][3]int{}
	for _, commmands := range commandsSplitAtInput {
		var1, _ := strconv.Atoi(strings.Split(commmands[5], " ")[2])
		var2, _ := strconv.Atoi(strings.Split(commmands[15], " ")[2])
		var3, _ := strconv.Atoi(strings.Split(commmands[4], " ")[2])
		vars = append(vars, [3]int{var1, var2, var3})
	}

	possibleNumbers := make(map[string]int)
	possibleNumbers[""] = 0
	for i := 0; i < 14; i++ {
		possibleNumbers = getAllPossibleNumbers(possibleNumbers, vars)
	}

	possibleNumbersInt := []int{}
	for possibleNumberString, z := range possibleNumbers {
		if z == 0 {
			possibleNumberInt, _ := strconv.Atoi(possibleNumberString)
			possibleNumbersInt = append(possibleNumbersInt, possibleNumberInt)
		}
	}
	sort.Ints(possibleNumbersInt)
	fmt.Println(possibleNumbersInt[len(possibleNumbersInt)-1])
	fmt.Println(possibleNumbersInt[0])

	//
	//possibleNumbers := make(map[string]int)
	//possibleNumbers[""] = 10
	//testNumber := []int{9, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
	//fmt.Println(checkNumber(testNumber, vars, 0, 0))
	//alu := make(map[rune]int)
	//alu['w'] = 0
	//alu['x'] = 0
	//alu['y'] = 0
	//alu['z'] = 0
	//for i, digit := range testNumber {
	//	step(&alu, commandsSplitAtInput[i], digit)
	//}
	//fmt.Println(alu)

}

func getAllPossibleNumbers(possibleNumbers map[string]int, vars [][3]int) map[string]int {
	newPossibleNumbers := make(map[string]int)
	for possibleNumber, z := range possibleNumbers {
		tmpNewPossibleNumber := getPossibleNumbers(vars, z, possibleNumber)
		for k, v := range tmpNewPossibleNumber {
			newPossibleNumbers[k] = v
		}
	}
	return newPossibleNumbers
}

func getPossibleNumbers(vars [][3]int, z int, pastDigits string) map[string]int {
	digitsWithZ := make(map[string]int)
	for i := 1; i < 10; i++ {
		resultZ := checkNumber([]int{i}, vars, z, len(pastDigits))
		if resultZ != -1 {
			digitsWithZ[pastDigits+strconv.Itoa(i)] = resultZ
		}
	}
	return digitsWithZ
}

func checkNumber(number []int, vars [][3]int, z int, amountPastDigits int) int {
	currentVars := amountPastDigits
	for _, digit := range number {
		if vars[currentVars][0] < 0 && (z%26)+vars[currentVars][0] != digit {
			return -1
		}
		z, _ = stepSimplified(z, digit, vars[currentVars])
		if z < 0 {
			return -1
		}
		currentVars++
	}
	return z
}

func stepSimplified(z, w int, vars [3]int) (int, int) {
	newZ := z / vars[2]
	if (z%26)+vars[0] == w {
		z = newZ
	} else {
		z = (26 * newZ) + vars[1] + w
	}
	return z, w
}

func step(alu *map[rune]int, commands []string, inputVal int) {
	for _, command := range commands {
		splitCommand := strings.Split(command, " ")
		switch splitCommand[0] {
		case "inp":
			(*alu)[rune(splitCommand[1][0])] = inputVal
			break
		case "mul":
			(*alu)[rune(splitCommand[1][0])] = (*alu)[rune(splitCommand[1][0])] * getRightValue(alu, splitCommand[2])
			break
		case "add":
			(*alu)[rune(splitCommand[1][0])] = (*alu)[rune(splitCommand[1][0])] + getRightValue(alu, splitCommand[2])
			break
		case "div":
			(*alu)[rune(splitCommand[1][0])] = (*alu)[rune(splitCommand[1][0])] / getRightValue(alu, splitCommand[2])
			break
		case "mod":
			(*alu)[rune(splitCommand[1][0])] = (*alu)[rune(splitCommand[1][0])] % getRightValue(alu, splitCommand[2])
			break
		case "eql":
			if (*alu)[rune(splitCommand[1][0])] == getRightValue(alu, splitCommand[2]) {
				(*alu)[rune(splitCommand[1][0])] = 1
			} else {
				(*alu)[rune(splitCommand[1][0])] = 0
			}
		}
	}
}

func getRightValue(alu *map[rune]int, commandRight string) int {
	intValue, err := strconv.Atoi(commandRight)
	if err != nil {
		return (*alu)[rune(commandRight[0])]
	} else {
		return intValue
	}
}
