package day12

import (
	"fmt"
	"strings"
	"adventofcode2022/utils"
	"adventofcode2022/utils/grid2d"
)

type cell struct {
	height   int
	distance int
}

type coord struct {
	x int
	y int
}

func Part1(input string) int {
	startCoord, grid := parseInput(input)
	fillGrid(grid)
	printGrid(grid)
	c := grid.Get(startCoord.x, startCoord.y)
	return c.distance
}

func Part2(input string) int {
	_, grid := parseInput(input)
	fillGrid(grid)
	min := utils.MaxInt
	for j := 0; j < grid.SizeY(); j++ {
		for i := 0; i < grid.SizeX(); i++ {
			c1 := grid.Get(i, j)
			if c1.height == 0 {
				min = utils.Min(min, c1.distance)
			}
		}
	}
	return min
}

func parseInput(input string) (coord, *grid2d.Grid[*cell]) {
	lines := strings.Split(input, "\n")
	grid := grid2d.NewGrid[*cell](len(lines[0]), len(lines), nil)
	startCoord := coord{-1, -1}
	for j, line := range lines {
		for i, c := range line {
			newCell := &cell{height: int(c - 'a'), distance: utils.MaxInt}
			if c == 'S' {
				newCell.height = 0
				startCoord.x = i
				startCoord.y = j
			} else if c == 'E' {
				newCell.height = 25
				newCell.distance = 0
			}
			grid.Set(i, j, newCell)
		}
	}
	return startCoord, grid
}

func fillGrid(grid *grid2d.Grid[*cell]) {
	done := false
	for !done {
		done = true
		for j := 0; j < grid.SizeY(); j++ {
			for i := 0; i < grid.SizeX(); i++ {
				c1 := grid.Get(i, j)
				if c1.distance == utils.MaxInt {
					continue
				}
				moves := [][]int{{0, 1}, {0, -1}, {1, 0}, {-1, 0}}
				for _, move := range moves {
					x2 := i + move[0]
					y2 := j + move[1]
					c2 := grid.Get(x2, y2)
					if c2 == nil {
						continue
					}
					if c2.height < c1.height-1 {
						continue
					}
					if c2.distance > c1.distance+1 {
						c2.distance = c1.distance + 1
						done = false
					}
				}
			}
		}
	}
}

func printGrid(grid *grid2d.Grid[*cell]) {
	r := grid.StringWithFormatter(func(c *cell, i, j int) string {
		if c.distance == utils.MaxInt {
			return "?? "
		}
		return fmt.Sprintf("%02d ", c.distance)
	})
	fmt.Println(r)
}