package main

import (
	"fmt"
	"os"

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

var cave h.CoordByteMap
var debug bool

func main() {
	debug = len(os.Args) > 1 && os.Args[1] == "--debug"
	inp := h.StdinToStringSlice()
	cave = h.StringSliceToCoordByteMap(inp)
	fmt.Println("# Part 1")
	part1()
	fmt.Println("# Part 2")
	part2()
}

var visited map[h.Coordinate]bool
var distance map[h.Coordinate]int
var curr h.Coordinate

func part1() {
	distance = make(map[h.Coordinate]int)
	visited = make(map[h.Coordinate]bool)
	for k := range cave.Field {
		visited[k] = false
		distance[k] = h.MAX_INT
	}
	curr = h.Coordinate{
		X: 0,
		Y: 0,
	}
	distance[curr] = 0
	visited[curr] = true

	work()
	fmt.Println(distance[h.Coordinate{X: cave.Width - 1, Y: cave.Height - 1}])
}

func work() {
	// Find the unvisited node with the smallest tentative distance
	var c h.Coordinate
	cDist := h.MAX_INT
	for k, v := range distance {
		if visited[k] {
			continue
		}
		if v < cDist {
			cDist = v
			c = k
		}
	}

	check := getNeighbors(c)
	for _, v := range check {
		if !visited[v] {
			if b, ok := distance[v]; !ok || b > distance[c]+getInt(v) {
				distance[v] = distance[c] + getInt(v)
			}
		}
	}
	visited[c] = true

	// if we still have unvisited nodes, recurse
	if moreWork() {
		work()
	}
}

func moreWork() bool {
	for i := range visited {
		if visited[i] == false {
			return true
		}
	}
	return false
}

func part2() {
	// Build the full cave
	expandCave(5)
	part1()
}

func getInt(c h.Coordinate) int {
	return int(cave.Opt(c, '0')) - int('0')
}

func getNeighbors(c h.Coordinate) []h.Coordinate {
	var ret []h.Coordinate
	for _, n := range []h.Coordinate{c.North(), c.East(), c.South(), c.West()} {
		if v := cave.Opt(n, 'Z'); v != 'Z' {
			ret = append(ret, n)
		}
	}
	return ret
}

func expandCave(size int) {
	oH, oW := cave.Height, cave.Width
	for i := 1; i < size; i++ {
		for y := 0; y < oH; y++ {
			for x := 0; x < oW; x++ {
				ny := (oH*i + y)
				prev := h.Coordinate{
					X: x,
					Y: (oH*(i-1) + y),
				}
				pv := cave.Get(prev)
				var v byte
				if pv == '9' {
					v = '1'
				} else {
					v = pv + 1
				}
				cave.Put(h.Coordinate{X: x, Y: ny}, v)
			}
		}
	}
	for y := 0; y < cave.Height; y++ {
		for i := 1; i < size; i++ {
			for x := 0; x < oW; x++ {
				nx := (oW*i + x)
				prev := h.Coordinate{
					X: (oW*(i-1) + x),
					Y: y,
				}
				pv := cave.Get(prev)
				var v byte
				if pv == '9' {
					v = '1'
				} else {
					v = pv + 1
				}
				cave.Put(h.Coordinate{X: nx, Y: y}, v)
			}
		}
	}
}
