package aoc2022

import println
import readInput
import splitInts

/*
--- Day 5: Supply Stacks ---
https://adventofcode.com/2022/day/5
*/
fun main() {

    val inlineTestInput = """    [D]    
[N] [C]    
[Z] [M] [P]
 1   2   3 

move 1 from 2 to 1
move 3 from 1 to 3
move 2 from 2 to 1
move 1 from 1 to 2
"""

    data class StackOp(val num: Int, val src: Int, val dest: Int)

    fun part1(input: List<String>): String {
        val numItems = input.first { it.startsWith(" 1") }.splitInts().max()
        val stackOps = input.filter { it.startsWith("move") }.map {
            val line = it.split(" ")
            StackOp(line[1].toInt(), line[3].toInt() - 1, line[5].toInt() - 1)
        }
        val stackLines = input.filter { it.contains('[') }

        val stacks = Array<ArrayDeque<Char>>(numItems) { ArrayDeque() }

        for (p in stackLines.lastIndex downTo 0) {
            for (i in 0 until numItems) {
                if (stackLines[p].length > i * 4 + 1 && stackLines[p][i * 4 + 1] != ' ') {
                    stacks[i].addFirst(stackLines[p][i * 4 + 1])
                }
            }
        }
        for (op in stackOps) {
            for (i in 0 until op.num) {
                stacks[op.dest].addFirst(stacks[op.src].removeFirst())
            }
        }
        return stacks.map { it.first() }.joinToString(separator = "") { it.toString() }
    }

    fun part2(input: List<String>): String {
        val numItems = input.first { it.startsWith(" 1") }.splitInts().max()
        val stackOps = input.filter { it.startsWith("move") }.map {
            val line = it.split(" ")
            StackOp(line[1].toInt(), line[3].toInt() - 1, line[5].toInt() - 1)
        }
        val stackLines = input.filter { it.contains('[') }

        val stacks = Array<ArrayDeque<Char>>(numItems) { ArrayDeque() }

        for (p in stackLines.lastIndex downTo 0) {
            for (i in 0 until numItems) {
                if (stackLines[p].length > i * 4 + 1 && stackLines[p][i * 4 + 1] != ' ') {
                    stacks[i].addFirst(stackLines[p][i * 4 + 1])
                }
            }
        }
        for (op in stackOps) {
            val tmpList = ArrayDeque<Char>()
            for (i in 0 until op.num) {
                tmpList.addFirst(stacks[op.src].removeFirst())
            }
            for (c in tmpList) {
                stacks[op.dest].addFirst(c)
            }
        }
        return stacks.map { it.first() }.joinToString(separator = "") { it.toString() }
    }

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

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