package main

import (
	"bufio"
	"container/heap"
	"fmt"
	"os"
)

const bailFactor = 8

func main() {
	inp := StdinToStringSlice()
	fmt.Printf("# Part 1\nRisk level: %d\n", RiskLevel(inp))
	fmt.Printf("# Part 2\nRescue minutes: %d\n", Rescue(inp))
}

func RiskLevel(input []string) int {
	m := NewMap(input)
	sum := 0
	for y := 0; y <= m.target.y; y++ {
		for x := 0; x <= m.target.x; x++ {
			sum += m.Type(x, y)
		}
	}
	return sum
}

func Rescue(input []string) int {
	m := NewMap(input)
	queue := PriorityQueue{
		&Item{pos: coord{0, 0}, time: 0, equip: ToolTorch},
	}
	heap.Init(&queue)

	type step struct {
		coord coord
		equip int
	}

	distances := map[step]int{
		step{coord: coord{0, 0}, equip: ToolTorch}: 0,
	}

	for len(queue) > 0 {
		item := (heap.Pop(&queue)).(*Item)
		if item.pos.x == m.target.x && item.pos.y == m.target.y && item.equip == ToolTorch {
			return item.time
		}

		// Check if we're wandering too far away
		if item.pos.x > bailFactor*m.target.x || item.pos.y > bailFactor*m.target.y {
			continue
		}

		if t, ok := distances[step{coord: item.pos, equip: item.equip}]; ok && t < item.time {
			continue
		}

		for _, n := range m.Neighbors(item.pos, item.equip) {
			d := step{coord: n.pos, equip: n.equip}
			if t, ok := distances[step{coord: n.pos, equip: n.equip}]; !ok || item.time+n.time < t {
				distances[d] = item.time + n.time
				heap.Push(&queue, &Item{pos: n.pos, time: item.time + n.time, equip: n.equip})
			}
		}
	}

	return 0
}

func StdinToStringSlice() []string {
	var input []string
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		input = append(input, scanner.Text())
	}
	return input
}
