package main

import (
	"fmt"
	"os"
	"strings"
	"log"
	"strconv"
	"time"
)

var aFact, bFact int
var doPart1, judgeIsDone bool
var score int

func main() {
	var doPart1 bool
	// My input
	aStart := 679
	bStart := 771
	for i := range os.Args {
		if strings.HasPrefix(os.Args[i], "-a") {
			pts := strings.Split(os.Args[i], "=")
			if len(pts) == 2 {
				aStart = Atoi(pts[1])
			}
		} else if strings.HasPrefix(os.Args[i], "-b") {
			pts := strings.Split(os.Args[i], "=")
			if len(pts) == 2 {
				bStart = Atoi(pts[1])
			}
		}
		if os.Args[i] == "-test" {
			aStart = 65
			bStart = 8921
		}
		if os.Args[i] == "-1" {
			doPart1 = true
		}
	}

	aFact = 16807
	bFact = 48271

	if doPart1 {
		solve(aStart, 1, bStart, 1, 40000000)
	} else {
		solve(aStart, 4, bStart, 8, 5000000)
	}
}

func solve(aStart, aMod, bStart, bMod, target int) {
	fmt.Println("= Part 2 =")
	fmt.Print("(A ",aStart,"; B ",bStart,")\n")

	judgeChanA := make(chan int)
	judgeChanB := make(chan int)

	// The Judge Process
	go judgeWork(target, judgeChanA, judgeChanB)

	// Generator A Process
	go genWork(aStart, aFact, aMod, judgeChanA)
	// Generator B Process
	go genWork(bStart, bFact, bMod, judgeChanB)

	// Wait for the judge to say he's done
	for !judgeIsDone {
		time.Sleep(500)
	}
	fmt.Println("\nFinal",score)
}

func judgeWork(target int, judgeChanA, judgeChanB chan int) {
	for i := 0; i < target; i++ {
		aVal := <-judgeChanA
		bVal := <-judgeChanB
		if i % (target / 25) == 0 {
			fmt.Print(".")
		}
		if judge(aVal, bVal) {
			score++
		}
	}
	judgeIsDone = true
}

func genWork(start, fact, mod int, judgeChan chan int) {
	for !judgeIsDone {
		start = getNextVal(start, fact)
		if start % mod == 0 {
			judgeChan <- start
		}
	}
}

func getNextVal(prev, fact int) int {
	return (prev * fact) % 2147483647
}

func judge(aVal, bVal int) bool {
	aBin, bBin := GetBinaryString(aVal), GetBinaryString(bVal)
	return aBin[len(aBin)-16:] == bBin[len(bBin)-16:]
}

func GetBinaryString(inp int) string {
	return fmt.Sprintf("%032b", inp)
}

func Atoi(i string) int {
	var ret int
	var err error
	if ret, err = strconv.Atoi(i); err != nil {
		log.Fatal("Invalid Atoi")
	}
	return ret
}
