package day14

import (
	"adventofcode2024/utils"
	grid "adventofcode2024/utils/grid2d"
	"fmt"
	"regexp"
	"strings"
)

type Robot struct {
	velocity [2]int
}

const SIZE_X = 101
const SIZE_Y = 103

func Part1(input string) int {
	grid := grid.NewGrid(SIZE_X, SIZE_Y, []Robot{})
	pattern := `p=(-?\d+),(-?\d+) v=(-?\d+),(-?\d+)`
	re := regexp.MustCompile(pattern)
	lines := strings.Split(input, "\n")
	for _, line := range lines {
		matches := re.FindStringSubmatch(line)
		fmt.Println(matches)
		x := utils.MustAtoi(matches[1])
		y := utils.MustAtoi(matches[2])
		x1 := utils.MustAtoi(matches[3])
		y1 := utils.MustAtoi(matches[4])
		robots := grid.Get(x, y)
		grid.Set(x, y, append(robots, Robot{velocity: [2]int{x1, y1}}))
	}
	fmt.Println(grid.StringWithFormatter(formatter))
	ng := ticks(grid, 100)
	fmt.Println(ng.StringWithFormatter(formatter))
	
	return get_saftey(ng)
}

func Part2(input string) int {
	min_safe := 240096000
	min_i := 0
	max_safe := 0
	max_i := 0
	grid := grid.NewGrid(SIZE_X, SIZE_Y, []Robot{})
	pattern := `p=(-?\d+),(-?\d+) v=(-?\d+),(-?\d+)`
	re := regexp.MustCompile(pattern)
	lines := strings.Split(input, "\n")
	for _, line := range lines {
		matches := re.FindStringSubmatch(line)
		fmt.Println(matches)
		x := utils.MustAtoi(matches[1])
		y := utils.MustAtoi(matches[2])
		x1 := utils.MustAtoi(matches[3])
		y1 := utils.MustAtoi(matches[4])
		robots := grid.Get(x, y)
		grid.Set(x, y, append(robots, Robot{velocity: [2]int{x1, y1}}))
	}
	fmt.Println(grid.StringWithFormatter(formatter))
	max_safe = get_saftey(grid)
	for i:=0;i<7380;i++ {
		ng := ticks(grid, 1)
		safe := get_saftey(ng)
		if safe > max_safe {
			max_safe = safe
			max_i = i
		}
		if safe < min_safe {
			min_safe = safe
			min_i = i
		}
		grid = ng
	}
	for i:=7380;i<7390;i++{
		ng := ticks(grid, 1)
		fmt.Println(ng.StringWithFormatter(formatter))
		grid = ng
	}
	fmt.Println(grid.StringWithFormatter(formatter))
	fmt.Printf("max_safe: %d max_i: %d min_safe: %d min_i:%d", max_safe, max_i, min_safe, min_i)
	return get_saftey(grid)
}

func formatter(robots []Robot, x int, y int) string {
	if len(robots) == 0 {
		return " "
	}
	return "*"
}

func ticks(g *grid.Grid[[]Robot], seconds int) *grid.Grid[[]Robot] {
	ng := grid.NewGrid(SIZE_X, SIZE_Y, []Robot{})
	for y := 0; y < g.SizeY(); y++ {
		for x := 0; x < g.SizeX(); x++ {
			for _, robot := range g.Get(x, y) {
				nx := x + ((robot.velocity[0] * seconds) % SIZE_X)
				if nx < 0 {
					nx = g.SizeX() + nx
				}
				if nx >= g.SizeX() {
					nx = nx - g.SizeX()
				}
				ny := y + ((robot.velocity[1] * seconds) % SIZE_Y)
				if ny < 0 {
					ny = g.SizeY() + ny
				}
				if ny >= g.SizeY() {
					ny = ny - g.SizeY()
				}
				robots := ng.Get(nx, ny)
				ng.Set(nx, ny, append(robots, robot))
			}
		}
	}
	return ng
}

func get_saftey(grid *grid.Grid[[]Robot]) int {
	robots := 0
	val := 1
	for y := 0; y < grid.SizeY()/2; y++ {
		for x := 0; x < grid.SizeX()/2; x++ {
			robots += len(grid.Get(x, y))
		}
	}
	val *= robots
	robots = 0
	for y := 0; y < grid.SizeY()/2; y++ {
		for x := grid.SizeX()/2 + 1; x < grid.SizeX(); x++ {
			robots += len(grid.Get(x, y))
		}
	}
	val *= robots
	robots = 0
	for y := grid.SizeY()/2 + 1; y < grid.SizeY(); y++ {
		for x := grid.SizeX()/2 + 1; x < grid.SizeX(); x++ {
			robots += len(grid.Get(x, y))
		}
	}
	val *= robots
	robots = 0
	for y := grid.SizeY()/2 + 1; y < grid.SizeY(); y++ {
		for x := 0; x < grid.SizeX()/2; x++ {
			robots += len(grid.Get(x, y))
		}
	}
	val *= robots
	return val
}