package main

import (
	"AOC2021/src/helper"
	"fmt"
	"os"
)

//0 -> Unknown
//1 -> true
//2 -> false
type field struct {
	height   int
	lowPoint int
	basin    [2]int
}

func main() {
	args := os.Args[1:]
	input, err := helper.GetInput(args[0])
	if err != nil {
		fmt.Println(err)
	}
	heightMap := [100][100]field{}
	for i, line := range input {
		for j, r := range line {
			heightMap[i][j] = field{int(r - '0'), 0, [2]int{-1,-1}}
		}
	}
	sum := 0
	amountOfBasinSet := 0
	//find Lowpoints
	for i, _ := range heightMap {
		for j, _ := range heightMap[i] {
			checkFieldLowpoint(&heightMap, j, i)
			if heightMap[i][j].lowPoint == 1 {
				sum += heightMap[i][j].height + 1
				amountOfBasinSet++
			}
		}
	}
	fmt.Println(sum)

	//find Basin
	for amountOfBasinSet < len(heightMap) * len(heightMap[0]) {
		for i, _ := range heightMap {
			for j, _ := range heightMap[i] {
				if heightMap[i][j].height == 9 && checkBasin(heightMap[i][j]) != 2 {
					heightMap[i][j].basin = [2]int{-2, -2}
					amountOfBasinSet++
				}
				checkFieldBasin(&heightMap, j, i, &amountOfBasinSet)
			}
		}
	}

	basinMap := make(map[string]int)
	for i, _ := range heightMap {
		for j, _ := range heightMap[i] {
			if checkBasin(heightMap[i][j]) == 1 {
				fieldBasin := heightMap[i][j].basin
				basinString := fmt.Sprintf("X%d Y%d", fieldBasin[0], fieldBasin[1])
				basinMap[basinString] += 1
			}
		}
	}
	rankedMap := helper.RankByValueAmount(basinMap)
	fmt.Println(rankedMap[0].Value * rankedMap[1].Value * rankedMap[2].Value)


}

func checkFieldBasin(heightMap *[100][100]field, x int, y int, amountOfBasinSet *int) {
	if checkBasin(heightMap[y][x]) != 0  {
		return
	}
	directions := [4][2]int{{0, -1}, {+1, 0}, {0, 1}, {-1, 0}}
	for _, direction := range directions {
		px := x + direction[0]
		py := y + direction[1]
		if py < len(heightMap) && py >= 0 && px < len(heightMap[0]) && px >= 0 {
			if checkBasin(heightMap[py][px]) == 1{
				heightMap[y][x].basin = heightMap[py][px].basin
				*amountOfBasinSet++
				return
			}
		}
	}
}

//0 no Basin Set
//1 Basin Set
//2 no Basin Exists
func checkBasin(inputField field) int {
	fieldBasinX := inputField.basin[0]
	fieldBasinY := inputField.basin[1]
	if fieldBasinY == -1 || fieldBasinX == -1 {
		return 0
	}
	if fieldBasinY == -2 || fieldBasinX == -2 {
		return 2
	}
	return 1
}

func checkFieldLowpoint(heightMap *[100][100]field, x int, y int) {
	if heightMap[y][x].lowPoint == 2 {
		return
	}
	directions := [4][2]int{{0, -1}, {+1, 0}, {0, 1}, {-1, 0}}
	fieldHeight := heightMap[y][x].height
	for _, direction := range directions {
		px := x + direction[0]
		py := y + direction[1]
		if py < len(heightMap) && py >= 0 && px < len(heightMap[0]) && px >= 0 {
			if heightMap[py][px].height >= fieldHeight {
				heightMap[py][px].lowPoint = 2
			}
			if heightMap[py][px].height <= fieldHeight {
				heightMap[y][x].lowPoint = 2
				return
			}
		}
	}
	heightMap[y][x].lowPoint = 1
	heightMap[y][x].basin = [2]int{x,y}
	return
}
