package aoc2024

import println
import readInput

/*
--- Day 5: Print Queue ---
https://adventofcode.com/2024/day/5
*/
fun main() {

    val inlineTestInput = """
47|53
97|13
97|61
97|47
75|29
61|13
75|53
29|13
97|29
53|29
61|53
97|53
61|29
47|13
75|47
97|75
47|61
75|61
47|29
75|13
53|13

75,47,61,53,29
97,61,53,29,13
75,29,13
75,97,47,61,53
61,13,29
97,13,75,29,47
"""

    fun part1(input: List<String>): Int {
        val beforeList = Array(100) { HashSet<Int>() }
        var idx = 0
        while (input[idx].isNotEmpty()) {
            val (page, min) = input[idx++].split('|').map(String::toInt)
            beforeList[page].add(min)
        }
        idx++
        var sum = 0
        for (i in idx until input.size) {
            val pages = input[i].split(',').map(String::toInt)
            val visited = HashSet<Int>()
            var inOrder = true
            for (p in pages) {
                val good = beforeList[p].none { visited.contains(it) }
                if (!good) {
                    inOrder = false
                    break
                }
                visited.add(p)
            }
            if (inOrder) {
                sum += pages[pages.size / 2]
            }
        }
        return sum
    }

    fun part2(input: List<String>): Int {
        val beforeList = Array(100) { HashSet<Int>() }
        val afterList = Array(100) { HashSet<Int>() }
        var idx = 0
        while (input[idx].isNotEmpty()) {
            val (page, min) = input[idx++].split('|').map(String::toInt)
            beforeList[page].add(min)
            afterList[min].add(page)
        }
        idx++
        var sum = 0
        for (i in idx until input.size) {
            var fixedIt = false
            val pages = input[i].split(',').map(String::toInt).toMutableList()
            do {
                val visited = HashSet<Int>()
                var inOrder = true
                for ((pi, p) in pages.withIndex()) {
                    val badOnes = beforeList[p].filter { visited.contains(it) }
                    if (badOnes.isNotEmpty()) {
                        fixedIt = true
                        inOrder = false
                        for (b in badOnes) {
                            for (bi in pi - 1 downTo 0) {
                                if (pages[bi] == b) {
                                    pages[bi] = pages[pi]
                                    pages[pi] = b
                                    break
                                }
                            }
                        }
                    }
                    visited.add(p)
                }
            } while (!inOrder)
            if (fixedIt) {
                sum += pages[pages.size / 2]
            }
        }
        return sum
    }

    // test if implementation meets criteria from the description, like:
    val testInput = inlineTestInput.trim().reader().readLines()
    //val testInput = readInput("aoc2024/Day05_test")
    val testInputPart1Result = part1(testInput)
    println("Part 1 Test: $testInputPart1Result")
    val testInputPart2Result = part2(testInput)
    println("Part 2 Test: $testInputPart2Result")
    check(testInputPart1Result == 143)
    check(testInputPart2Result == 123)

    val input = readInput("aoc2024/Day05")
    part1(input).println()
    part2(input).println()
}
