package main

import (
	"fmt"
	"os"
	"strings"

	intcode "git.bullercodeworks.com/brian/adventofcode/2019/intcode-processor"
	helpers "git.bullercodeworks.com/brian/adventofcode/helpers"
)

func main() {
	inp := helpers.StdinToString()
	var prog []int
	for _, v := range strings.Split(inp, ",") {
		prog = append(prog, helpers.Atoi(v))
	}
	part1(prog)
	part2(prog)
}

func part1(prog []int) {
	p := intcode.NewProgram(prog)
	p.Run()
	if p.State() == intcode.RET_ERR {
		fmt.Println(p.Error())
	}
	fmt.Println("Part 1:", p.GetProgramValueAt(0))
}

func part2(prog []int) {
	runProg := func(n, v int, prog []int) int {
		progcpy := make([]int, len(prog))
		copy(progcpy, prog)
		progcpy[1] = n
		progcpy[2] = v
		p := intcode.NewProgram(progcpy)
		p.Run()
		return p.GetProgramValueAt(0)
	}
	target := 19690720
	zeroVal := runProg(0, 0, prog)
	nDiff := runProg(1, 0, prog) - zeroVal
	vDiff := runProg(0, 1, prog) - zeroVal
	fmt.Println("Part 2:", ((target - zeroVal) / nDiff), ((target%nDiff)-(zeroVal%nDiff))/vDiff)
}

func slowestPart2(prog []int) {
	for n := 0; n <= 99; n++ {
		for v := 0; v <= 99; v++ {
			progcpy := make([]int, len(prog))
			copy(progcpy, prog)
			progcpy[1], progcpy[2] = n, v
			for i := 0; i < len(progcpy); i += 4 {
				switch progcpy[i] {
				case 1:
					progcpy[progcpy[i+3]] = add(progcpy[i+1], progcpy[i+2], progcpy)
				case 2:
					progcpy[progcpy[i+3]] = mult(progcpy[i+1], progcpy[i+2], progcpy)
				case 99:
					if progcpy[0] == 19690720 {
						fmt.Println("Found value:", n, v)
						fmt.Println("Answer:", (100*n + v))
						os.Exit(0)
					} else {
						fmt.Println(n, v, ":", progcpy[0])
					}
					i = len(progcpy)
				}
			}
		}
	}
}

func slowPart2(prog []int) {
	target := 19690720
	useN := 0
	for n := 0; n <= 99; n++ {
		progcpy := make([]int, len(prog))
		copy(progcpy, prog)
		progcpy[1] = n
		for i := 0; i < len(progcpy); i += 4 {
			switch progcpy[i] {
			case 1:
				progcpy[progcpy[i+3]] = add(progcpy[i+1], progcpy[i+2], progcpy)
			case 2:
				progcpy[progcpy[i+3]] = mult(progcpy[i+1], progcpy[i+2], progcpy)
			case 99:
				i = len(progcpy)
			}
		}
		if progcpy[0] > target {
			useN = n - 1
			break
		}
	}
	for v := 0; v <= 99; v++ {
		progcpy := make([]int, len(prog))
		copy(progcpy, prog)
		progcpy[1] = useN
		progcpy[2] = v
		for i := 0; i < len(progcpy); i += 4 {
			switch progcpy[i] {
			case 1:
				progcpy[progcpy[i+3]] = add(progcpy[i+1], progcpy[i+2], progcpy)
			case 2:
				progcpy[progcpy[i+3]] = mult(progcpy[i+1], progcpy[i+2], progcpy)
			case 99:
				if progcpy[0] == target {
					fmt.Println("Found value:", useN, v)
					fmt.Println("Answer:", (100*useN + v))
					os.Exit(0)
				}
				i = len(progcpy)
			}
		}
	}
}

func printState(prog []int) {
	fmt.Println(prog)
}

func add(i, j int, prog []int) int {
	return prog[i] + prog[j]
}

func mult(i, j int, prog []int) int {
	return prog[i] * prog[j]
}
