package main

import (
	"fmt"

	h "git.bullercodeworks.com/brian/adventofcode/helpers"
)

func main() {
	inp := h.StdinToStringSlice()
	// size, bytes := 6, 12 // Test Input
	size, bytes := 70, 1024 // Actual Input
	part1(inp, size, bytes)
	fmt.Println()
	part2(inp, size, bytes)
}

func part1(inp []string, size, bytes int) {
	// bytes:= 12 // Test Input
	m := h.NewCoordByteMap()
	m.BRX, m.BRY = size, size
	drops := parseInput(inp)
	for i := 0; i < bytes; i++ {
		m.Put(drops[i], '#')
	}

	fmt.Println("# Part 1")
	fmt.Println(FindShortestPath(m, func(a, b State) bool {
		return a.cnt+a.dist < b.cnt+b.dist
	}))
}

func part2(inp []string, size, bytes int) {
	m := h.NewCoordByteMap()
	m.BRX, m.BRY = size, size
	drops := parseInput(inp)
	// We can skip up to 'bytes', part1 showed that that many work
	for i := 0; i < bytes; i++ {
		m.Put(drops[i], '#')
	}

	fmt.Println("# Part 2")
	for tm, b := range drops[bytes+1:] {
		m.Put(b, '#')
		if FindShortestPath(m, func(a, b State) bool {
			return a.dist < b.dist
		}) == -1 {
			fmt.Printf("Blocked at %d: %s\n", bytes+tm, b.String())
			return
		}
	}
}

func parseInput(inp []string) []h.Coordinate {
	var ret []h.Coordinate
	for i := range inp {
		c := h.Coordinate{}
		fmt.Sscanf(inp[i], "%d,%d", &c.X, &c.Y)
		ret = append(ret, c)
	}
	return ret
}

type State struct {
	c    h.Coordinate
	cnt  int
	dist int
}

func FindShortestPath(inp h.CoordByteMap, less func(a, b State) bool) int {
	visited := make(map[h.Coordinate]State)
	end := h.Coordinate{X: inp.BRX, Y: inp.BRY}
	start := h.Coordinate{X: 0, Y: 0}
	startState := State{
		c:    start,
		cnt:  0,
		dist: start.Distance(end),
	}
	queue := h.NewHeap[State](less)
	queue.Push(startState)

	for queue.Len() > 0 {
		step := queue.Pop()
		if step.c.Equals(end) {
			visited[end] = step
			break
		}

		if _, ok := visited[step.c]; ok {
			continue
		}
		visited[step.c] = step
		// Find next steps
		for _, n := range step.c.GetOrthNeighbors() {
			if !inp.ContainsCoord(n) || inp.Get(n) == '#' {
				continue
			}
			if _, ok := visited[n]; ok {
				continue
			}
			nState := State{
				c:    n,
				cnt:  step.cnt + 1,
				dist: n.Distance(end),
			}
			queue.Push(nState)
		}
	}
	if v, ok := visited[end]; ok {
		return v.cnt
	}
	return -1
}