package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"time"

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

var a1, a2, a3, a4, a5 *intcode.Program

func main() {
	progFileName := "input"
	if len(os.Args) > 1 {
		progFileName = os.Args[1]
	}
	dat, err := ioutil.ReadFile(progFileName)
	if err != nil {
		fmt.Println("Error reading program file:", err.Error())
		os.Exit(1)
	}
	var prog []int
	stringDat := strings.TrimSpace(string(dat))
	for _, v := range strings.Split(stringDat, ",") {
		prog = append(prog, helpers.Atoi(v))
	}

	part1(prog)
	part2(prog)
}

func part1(prog []int) {
	allPerms := helpers.IntPermutations([]int{0, 1, 2, 3, 4})
	var bestPerm []int
	max := 0
	for k := range allPerms {
		a1 = intcode.NewProgram(prog)
		go a1.Run()
		a2 = intcode.NewProgram(prog)
		go a2.Run()
		a3 = intcode.NewProgram(prog)
		go a3.Run()
		a4 = intcode.NewProgram(prog)
		go a4.Run()
		a5 = intcode.NewProgram(prog)
		go a5.Run()

		wrk := pt1RunWithPhaseSettings(allPerms[k])
		if wrk > max {
			max = wrk
			bestPerm = allPerms[k]
		}
	}
	fmt.Println("Part 1 BEST: ", bestPerm, max)
}

func pt1RunWithPhaseSettings(settings []int) int {
	go func() {
		a1.Input(settings[0])
		a1.Input(0)
		a2.Input(settings[1])
		a2.Input(a1.Output())
		a3.Input(settings[2])
		a3.Input(a2.Output())
		a4.Input(settings[3])
		a4.Input(a3.Output())
		a5.Input(settings[4])
		a5.Input(a4.Output())
	}()
	for !a5.NeedsOutput() {
		time.Sleep(1)
	}
	return a5.Output()
}

func part2(prog []int) {
	allPerms := helpers.IntPermutations([]int{5, 6, 7, 8, 9})
	var bestPerm []int
	max := 0
	for k := range allPerms {
		a1 = intcode.NewProgram(prog)
		a1.Reset()
		go a1.Run()
		a2 = intcode.NewProgram(prog)
		a2.Reset()
		go a2.Run()
		a3 = intcode.NewProgram(prog)
		a3.Reset()
		go a3.Run()
		a4 = intcode.NewProgram(prog)
		a4.Reset()
		go a4.Run()
		a5 = intcode.NewProgram(prog)
		a5.Reset()
		go a5.Run()

		wrk := pt2RunWithPhaseSettings(allPerms[k])
		if wrk > max {
			max = wrk
			bestPerm = allPerms[k]
		}
	}
	fmt.Println("Part 2 BEST: ", bestPerm, max)
}

func pt2RunWithPhaseSettings(settings []int) int {
	a1.Input(settings[0])
	a2.Input(settings[1])
	a3.Input(settings[2])
	a4.Input(settings[3])
	a5.Input(settings[4])

	a5Out := 0
	for {
		a1.Input(a5Out)
		a2.Input(a1.Output())
		a3.Input(a2.Output())
		a4.Input(a3.Output())
		a5.Input(a4.Output())
		for !a5.NeedsOutput() {
			time.Sleep(1)
		}
		a5Out = a5.Output()
		// Wait until amp 5 is either done, or waiting
		for a5.State() != intcode.RET_DONE && !a5.NeedsInput() {
			time.Sleep(1)
		}
		if a5.State() == intcode.RET_DONE {
			break
		}
	}
	return a5Out
}
