package main

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/ImportedReality/aocutils"
)

var filename = "input.txt"

const gridX, gridY = 101, 103
const maxX, maxY = 100, 102

const part1Seconds = 100
const part2Seconds = 10000

type Coord aocutils.Coordinate

var robotRegexp = regexp.MustCompile("p=(-?\\d+,-?\\d+)\\sv=(-?\\d+,-?\\d+)")

type Robot struct {
	position, velocity Coord
}

func main() {
	input := aocutils.ReadLines(filename)
	robots := parseRobots(input)
	part1(robots)
	robots = parseRobots(input)
	part2(robots)
}

func parseRobots(input []string) []Robot {
	robots := make([]Robot, 0)

	for _, line := range input {
		matches := robotRegexp.FindAllStringSubmatch(line, -1)
		pos := strings.Split(matches[0][1], ",")
		posX, posY := aocutils.StrToInt(pos[0]), aocutils.StrToInt(pos[1])
		vel := strings.Split(matches[0][2], ",")
		velX, velY := aocutils.StrToInt(vel[0]), aocutils.StrToInt(vel[1])

		robot := Robot{
			Coord{posX, posY}, Coord{velX, velY},
		}
		robots = append(robots, robot)
	}

	return robots
}

func part1(robots []Robot) {
	for range part1Seconds {
		for i := range robots {
			robot := robots[i]
			robot.position = getNewRobotPosition(robot)
			robots[i] = robot
		}
	}
	a, b, c, d := countRobotsInQuadrants(robots)
	saftetyFactor := a * b * c * d
	fmt.Printf("After %d seconds the safety factor is: %d\n", part1Seconds, saftetyFactor)
}

func part2(robots []Robot) {
	lsf, t := 0, 0
	for i := range part2Seconds {
		for j := range robots {
			robot := robots[j]
			robot.position = getNewRobotPosition(robot)
			robots[j] = robot
		}
		a, b, c, d := countRobotsInQuadrants(robots)
		safetyFactor := a * b * c * d
		if safetyFactor != 0 && (safetyFactor < lsf || lsf == 0) {
			lsf, t = safetyFactor, i+1
			fmt.Printf("----- POTENTIAL PICTURE @ %d seconds -----", t)
			printBathroom(robots)
		}
	}

	fmt.Printf("After %d seconds, the lowest observed safety factor was %d at %d seconds.\n", part2Seconds, lsf, t)
	fmt.Println("This is the likliest time at which the robots have made a picture of a Christmas tree.")
}

func getNewRobotPosition(robot Robot) Coord {
	x, y := robot.position.X, robot.position.Y
	vX, vY := robot.velocity.X, robot.velocity.Y
	newX, newY := (x+vX)%gridX, (y+vY)%gridY
	if newX < 0 {
		abs := aocutils.Abs(newX)
		newX = maxX - (abs - 1)
	}
	if newY < 0 {
		abs := aocutils.Abs(newY)
		newY = maxY - (abs - 1)
	}
	return Coord{newX, newY}
}

func countRobotsInQuadrants(robots []Robot) (int, int, int, int) {
	a, b, c, d := 0, 0, 0, 0
	for _, robot := range robots {
		x, y := robot.position.X, robot.position.Y
		midX, midY := (gridX / 2), (gridY / 2)
		if x < midX && y < midY {
			a++
		}
		if x > midX && y < midY {
			b++
		}
		if x < midX && y > midY {
			c++
		}
		if x > midX && y > midY {
			d++
		}

	}
	return a, b, c, d
}

func printBathroom(robots []Robot) {
	bathroom := [gridY][gridX]int{}
	for _, robot := range robots {
		x, y := robot.position.X, robot.position.Y
		num := bathroom[y][x] + 1
		bathroom[y][x] = num
	}
	for y := range maxY {
		for x := range maxX {
			if bathroom[y][x] != 0 {
				fmt.Print(bathroom[y][x])
			} else {
				fmt.Print(".")
			}
		}
		println()
	}
}