package day10

import (
	"adventofcode2024/utils"
	"adventofcode2024/utils/grid2d"
	"adventofcode2024/utils/inputs"
	"fmt"
)

type Position struct {
	height int
	summit bool
}

func Part1(input string) int {
	score := 0
	i := 0
	grid := inputs.ToGrid2D(input, "\n", "", Position{height: -1}, func(c string) Position { return Position{summit: false, height: utils.MustAtoi(c)} })
	fmt.Println(grid.StringWithFormatter(formatter))
	for y := 0; y < grid.SizeY(); y++ {
		for x := 0; x < grid.SizeX(); x++ {
			position := grid.Get(x, y)
			if position.height != 0 {
				continue
			}
			wipe_summit(grid)
			score += getScore(x, y, 0, 0, grid)
			fmt.Printf("Trailhead %d (%d,%d) paths: %d\n", i, x, y, score)
			i++
		}
	}
	return score
}

func Part2(input string) int {
	score := 0
	i := 0
	grid := inputs.ToGrid2D(input, "\n", "", Position{height: -1}, func(c string) Position { return Position{summit: false, height: utils.MustAtoi(c)} })
	fmt.Println(grid.StringWithFormatter(formatter))
	for y := 0; y < grid.SizeY(); y++ {
		for x := 0; x < grid.SizeX(); x++ {
			position := grid.Get(x, y)
			if position.height != 0 {
				continue
			}
			wipe_summit(grid)
			score += getScore2(x, y, 0, 0, grid)
			fmt.Printf("Trailhead %d (%d,%d) paths: %d\n", i, x, y, score)
			i++
		}
	}
	return score
}

func formatter(p Position, x int, y int) string {
	return fmt.Sprintf("%v", p.height)
}

func wipe_summit(grid *grid2d.Grid[Position]) {
	for y := 0; y < grid.SizeY(); y++ {
		for x := 0; x < grid.SizeX(); x++ {
			pos := grid.Get(x, y)
			if pos.height == 9 && pos.summit {
				pos.summit = false
				grid.Set(x, y, pos)
			}
		}
	}
}

func getScore(x, y, level, score int, grid *grid2d.Grid[Position]) int {
	directions := [][]int{{0, 1}, {0, -1}, {1, 0}, {-1, 0}}
	for _, dir := range directions {
		x1 := x + dir[0]
		y1 := y + dir[1]
		nposition := grid.Get(x1, y1)
		if level == 8 && nposition.height == 9 {
			score++
		}
		if nposition.height != level+1 {
			continue
		}
		score = getScore(x1, y1, nposition.height, score, grid)
	}
	return score
}

func getScore2(x, y, level, score int, grid *grid2d.Grid[Position]) int {
	directions := [][]int{{0, 1}, {0, -1}, {1, 0}, {-1, 0}}
	for _, dir := range directions {
		x1 := x + dir[0]
		y1 := y + dir[1]
		nposition := grid.Get(x1, y1)
		if level == 8 && nposition.height == 9 {
			score++
		}
		if nposition.height != level+1 {
			continue
		}
		score = getScore2(x1, y1, nposition.height, score, grid)
	}
	return score
}
