package main

import (
	"bufio"
	"log"
	"os"
	"sort"
	"strconv"
)

var directions = [][2]int{
	{-1, 0}, //left
	{1, 0},  //right
	{0, -1}, // up
	{0, 1},  // down
}

type Matrix [][]int

func main() {
	if err := myMain(); err != nil {
		log.Println(err)
	}
}

func myMain() error {
	// parse input.txt
	matrix, err := parseInput("input.txt")
	if err != nil {
		return err
	}

	log.Println(solveFirst(matrix))

	log.Println(solveSecond(matrix))

	return nil
}

type Seen map[[2]int]bool

func solveSecond(m Matrix) int {
	lows := m.getLows()

	var ret = make([]int, len(lows))
	var seen = Seen{}
	for i, item := range lows {
		ret[i] = seen.getBasinSize(m, item)
	}

	sort.Ints(ret)

	return ret[len(ret)-1] * ret[len(ret)-2] * ret[len(ret)-3]
}

func (seen Seen) getBasinSize(m Matrix, item [2]int) int {
	var ret int
	row, col := item[0], item[1]
	if seen[item] || m[row][col] == 9 {
		return 0
	}

	seen[item] = true
	ret++

	for _, d := range directions {
		if row+d[0] >= 0 && row+d[0] < len(m) &&
			col+d[1] >= 0 && col+d[1] < len(m[0]) {
			if m[row+d[0]][col+d[1]] != 9 {
				ret += seen.getBasinSize(m, [2]int{row + d[0], col + d[1]})
			}
		}
	}

	return ret
}

func (m Matrix) getLows() [][2]int {
	var ret [][2]int
	for row, line := range m {
		for col := range line {
			if m.checkLow(row, col) {
				ret = append(ret, [2]int{row, col})
			}
		}
	}
	return ret
}

func solveFirst(m Matrix) int {
	var ret int
	for row, line := range m {
		for col, item := range line {
			if m.checkLow(row, col) {
				ret += item + 1
			}
		}
	}
	return ret
}

func (m Matrix) checkLow(row, col int) bool {
	for _, d := range directions {
		if row+d[0] >= 0 && row+d[0] < len(m) &&
			col+d[1] >= 0 && col+d[1] < len(m[0]) {
			if m[row+d[0]][col+d[1]] <= m[row][col] {
				return false
			}
		}
	}
	return true
}

func parseInput(fileName string) (Matrix, error) {
	var ret = Matrix{}
	fd, err := os.Open(fileName)
	if err != nil {
		return ret, err
	}
	defer fd.Close()

	buf := bufio.NewScanner(fd)
	var line []int
	for buf.Scan() {
		for _, ch := range buf.Text() {
			tmp, err := strconv.Atoi(string(ch))
			if err != nil {
				return nil, err
			}
			line = append(line, tmp)
		}
		ret = append(ret, line)
		line = []int{}
	}

	return ret, nil
}