package aoc2022

import println
import readInput
import kotlin.math.max
import kotlin.math.min

/*
--- Day 8: Treetop Tree House ---
https://adventofcode.com/2022/day/8
*/
fun main() {

    val inlineTestInput = """
30373
25512
65332
33549
35390
"""

    fun parseInput(input: List<String>): IntArray {
        val dim = input.first().length
        val out = IntArray(dim * dim)
        for ((rnum, r) in input.withIndex()) {
            r.forEachIndexed { index, c -> out[rnum * dim + index] = c - '0' }
        }
        return out
    }

    fun createMaxArray(trees: IntArray, dim: Int, tcurr: (row: Int, col: Int) -> Int): IntArray {
        val maxArray = IntArray(dim * dim)
        for (row in 0 until dim) {
            var maxValue = -1
            for (col in 0 until dim) {
                val posCurr = tcurr(row, col)
                maxArray[posCurr] = maxValue
                maxValue = max(trees[posCurr], maxValue)
            }
        }
        return maxArray
    }

    fun part1(input: List<String>): Int {
        val dim = input.first().length
        val trees = parseInput(input)
        val west = createMaxArray(trees, dim) { row, col -> row * dim + col }
        val east = createMaxArray(trees, dim) { row, col -> row * dim + (dim - col - 1) }
        val north = createMaxArray(trees, dim) { row, col -> col * dim + row }
        val south = createMaxArray(trees, dim) { row, col -> (dim - col - 1) * dim + row }
        return trees.withIndex().count { (i, v) -> v > min(min(north[i], south[i]), min(west[i], east[i])) }
    }

    fun createVisArray(trees: IntArray, dim: Int, tcurr: (row: Int, col: Int) -> Int): IntArray {
        val visArray = IntArray(dim * dim)
        for (row in 0 until dim) {
            var visTrees = 0
            var posLast = -1
            for (col in 0 until dim) {
                val posCurr = tcurr(row, col)
// should have been possible with dynamic programming, but I'm too stupid to get it right, so brute force instead
//                visArray[posCurr] = if (posLast < 0) {
//                    0
//                } else if (trees[posCurr] > trees[posLast]) {
//                    ++visTrees
//                } else if (trees[posCurr] == trees[posLast]) {
//                    ++visTrees
//                    1
//                } else {
//                    visTrees = 1
//                    1
//                }
//                posLast = posCurr
                for (col2 in col - 1 downTo 0) {
                    val t = trees[tcurr(row, col2)]
                    visArray[posCurr]++
                    if (trees[posCurr] <= t) break
                }
            }
        }
        return visArray
    }

    fun part2(input: List<String>): Int {
        val dim = input.first().length
        val trees = parseInput(input)
        val west = createVisArray(trees, dim) { row, col -> row * dim + col }
        val east = createVisArray(trees, dim) { row, col -> row * dim + (dim - col - 1) }
        val north = createVisArray(trees, dim) { row, col -> col * dim + row }
        val south = createVisArray(trees, dim) { row, col -> (dim - col - 1) * dim + row }

//        println("West:")
//        west.asSequence().chunked(dim).joinToString("\n") { it.joinToString(",") }.println()
//
//        println("East:")
//        east.asSequence().chunked(dim).joinToString("\n") { it.joinToString(",") }.println()
//
//        println("North:")
//        north.asSequence().chunked(dim).joinToString("\n") { it.joinToString(",") }.println()
//
//        println("South:")
//        south.asSequence().chunked(dim).joinToString("\n") { it.joinToString(",") }.println()

        var maxValue = -1
        for (row in 0 until dim) {
            for (col in 0 until dim) {
                val p = row * dim + col
                maxValue = max(north[p] * south[p] * west[p] * east[p], maxValue)
            }
        }
        return maxValue
    }

    // test if implementation meets criteria from the description, like:
    val testInput = inlineTestInput.trim().reader().readLines()
    //val testInput = readInput("aoc2022/Day08_test")
    println("Part 1 Test: " + part1(testInput))
    println("Part 2 Test: " + part2(testInput))
    check(part1(testInput) == 21)
    check(part2(testInput) == 8)

    val input = readInput("aoc2022/Day08")
    part1(input).println()
    part2(input).println()
}
