package day13

import java.io.File

fun main() {
    val data = parse("src/main/kotlin/day13/Day13.txt")

    val answer1 = part1(data)
    val answer2 = part2(data)

    println("🎄 Day 13 🎄")

    println()

    println("[Part 1]")
    println("Answer: $answer1")

    println()

    println("[Part 2]")
    println("Answer: $answer2")
}

private sealed class Packet : Comparable<Packet> {
    data class Integer(val value: Int) : Packet() {
        override fun toString() = value.toString()

        override operator fun compareTo(other: Packet): Int =
            when (other) {
                is Integer -> value.compareTo(other.value)
                is List -> compareContents(listOf(this), other.values)
            }
    }

    data class List(val values: kotlin.collections.List<Packet>) : Packet() {
        override fun toString() = values.toString()

        override operator fun compareTo(other: Packet): Int =
            when (other) {
                is Integer -> compareContents(values, listOf(other))
                is List -> compareContents(values, other.values)
            }
    }

    companion object {
        fun of(n: Int) =
            Integer(n)

        fun of(vararg elements: Packet) =
            List(listOf(*elements))
    }
}

private fun compareContents(lhs: List<Packet>, rhs: List<Packet>): Int {
    for ((a, b) in lhs.zip(rhs)) {
        val result = a.compareTo(b)
        if (result != 0) {
            return result
        }
    }
    return lhs.size.compareTo(rhs.size)
}

@Suppress("SameParameterValue")
private fun parse(path: String): List<Packet> =
    File(path).readLines()
        .filterNot { it.isBlank() }
        .map { it.toPacket() }

private fun String.toPacket(): Packet =
    ParserState(source = this).packet()

private data class ParserState(
    val source: String,
    var current: Int = 0,
)

private fun ParserState.peek(): Char =
    source[current]

private fun ParserState.next(): Char =
    source[current++]

/*
 * packet ::= list | integer
 */
private fun ParserState.packet(): Packet {
    return if (peek() == '[') {
        list()
    } else {
        integer()
    }
}

/*
 * list ::= '[' (packet ',')* packet? ']'
 */
private fun ParserState.list(): Packet.List {
    val values = mutableListOf<Packet>()

    next() // skip '['
    while (peek() != ']') {
        values.add(packet())
        if (peek() == ',') {
            next()
        }
    }

    next() // skip ']'
    return Packet.List(values)
}

/*
 * integer ::= [0-9]+
 */
private fun ParserState.integer(): Packet.Integer {
    var result = 0
    while (peek().isDigit()) {
        result = result * 10 + next().digitToInt()
    }
    return Packet.Integer(result)
}

private fun part1(data: List<Packet>): Int {
    val indices = mutableListOf<Int>()
    for ((i, packet) in data.chunked(2).withIndex()) {
        val (lhs, rhs) = packet
        if (lhs < rhs) {
            indices.add(i + 1)
        }
    }
    return indices.sum()
}

private fun part2(data: List<Packet>): Int {
    val divider2 = Packet.of(Packet.of(2))
    val divider6 = Packet.of(Packet.of(6))
    val sorted = (data + listOf(divider2, divider6)).sorted()
    return (sorted.indexOf(divider2) + 1) * (sorted.indexOf(divider6) + 1)
}

Compiled from "Day13.kt"
public final class day13.Day13Kt {
  public static final void main();
    Code:
       0: ldc           #8                  // String src/main/kotlin/day13/Day13.txt
       2: invokestatic  #12                 // Method parse:(Ljava/lang/String;)Ljava/util/List;
       5: astore_0
       6: aload_0
       7: invokestatic  #16                 // Method part1:(Ljava/util/List;)I
      10: istore_1
      11: aload_0
      12: invokestatic  #19                 // Method part2:(Ljava/util/List;)I
      15: istore_2
      16: ldc           #21                 // String 🎄 Day 13 🎄
      18: getstatic     #27                 // Field java/lang/System.out:Ljava/io/PrintStream;
      21: swap
      22: invokevirtual #33                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      25: getstatic     #27                 // Field java/lang/System.out:Ljava/io/PrintStream;
      28: invokevirtual #35                 // Method java/io/PrintStream.println:()V
      31: ldc           #37                 // String [Part 1]
      33: getstatic     #27                 // Field java/lang/System.out:Ljava/io/PrintStream;
      36: swap
      37: invokevirtual #33                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      40: new           #39                 // class java/lang/StringBuilder
      43: dup
      44: invokespecial #42                 // Method java/lang/StringBuilder."<init>":()V
      47: ldc           #44                 // String Answer:
      49: invokevirtual #48                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      52: iload_1
      53: invokevirtual #51                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      56: invokevirtual #55                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      59: getstatic     #27                 // Field java/lang/System.out:Ljava/io/PrintStream;
      62: swap
      63: invokevirtual #33                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      66: getstatic     #27                 // Field java/lang/System.out:Ljava/io/PrintStream;
      69: invokevirtual #35                 // Method java/io/PrintStream.println:()V
      72: ldc           #57                 // String [Part 2]
      74: getstatic     #27                 // Field java/lang/System.out:Ljava/io/PrintStream;
      77: swap
      78: invokevirtual #33                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      81: new           #39                 // class java/lang/StringBuilder
      84: dup
      85: invokespecial #42                 // Method java/lang/StringBuilder."<init>":()V
      88: ldc           #44                 // String Answer:
      90: invokevirtual #48                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      93: iload_2
      94: invokevirtual #51                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      97: invokevirtual #55                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     100: getstatic     #27                 // Field java/lang/System.out:Ljava/io/PrintStream;
     103: swap
     104: invokevirtual #33                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     107: return

  private static final int compareContents(java.util.List<? extends day13.Packet>, java.util.List<? extends day13.Packet>);
    Code:
       0: aload_0
       1: checkcast     #67                 // class java/lang/Iterable
       4: aload_1
       5: checkcast     #67                 // class java/lang/Iterable
       8: invokestatic  #73                 // Method kotlin/collections/CollectionsKt.zip:(Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/List;
      11: invokeinterface #79,  1           // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
      16: astore_2
      17: aload_2
      18: invokeinterface #85,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      23: ifeq          71
      26: aload_2
      27: invokeinterface #89,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      32: checkcast     #91                 // class kotlin/Pair
      35: astore_3
      36: aload_3
      37: invokevirtual #94                 // Method kotlin/Pair.component1:()Ljava/lang/Object;
      40: checkcast     #96                 // class day13/Packet
      43: astore        4
      45: aload_3
      46: invokevirtual #99                 // Method kotlin/Pair.component2:()Ljava/lang/Object;
      49: checkcast     #96                 // class day13/Packet
      52: astore        5
      54: aload         4
      56: aload         5
      58: invokevirtual #103                // Method day13/Packet.compareTo:(Ljava/lang/Object;)I
      61: istore        6
      63: iload         6
      65: ifeq          17
      68: iload         6
      70: ireturn
      71: aload_0
      72: invokeinterface #107,  1          // InterfaceMethod java/util/List.size:()I
      77: aload_1
      78: invokeinterface #107,  1          // InterfaceMethod java/util/List.size:()I
      83: invokestatic  #113                // Method kotlin/jvm/internal/Intrinsics.compare:(II)I
      86: ireturn

  private static final java.util.List<day13.Packet> parse(java.lang.String);
    Code:
       0: new           #122                // class java/io/File
       3: dup
       4: aload_0
       5: invokespecial #125                // Method java/io/File."<init>":(Ljava/lang/String;)V
       8: aconst_null
       9: iconst_1
      10: aconst_null
      11: invokestatic  #131                // Method kotlin/io/FilesKt.readLines$default:(Ljava/io/File;Ljava/nio/charset/Charset;ILjava/lang/Object;)Ljava/util/List;
      14: checkcast     #67                 // class java/lang/Iterable
      17: astore_1
      18: nop
      19: iconst_0
      20: istore_2
      21: aload_1
      22: astore_3
      23: new           #133                // class java/util/ArrayList
      26: dup
      27: invokespecial #134                // Method java/util/ArrayList."<init>":()V
      30: checkcast     #136                // class java/util/Collection
      33: astore        4
      35: iconst_0
      36: istore        5
      38: aload_3
      39: invokeinterface #137,  1          // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      44: astore        6
      46: aload         6
      48: invokeinterface #85,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      53: ifeq          99
      56: aload         6
      58: invokeinterface #89,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      63: astore        7
      65: aload         7
      67: checkcast     #139                // class java/lang/String
      70: astore        8
      72: iconst_0
      73: istore        9
      75: aload         8
      77: checkcast     #141                // class java/lang/CharSequence
      80: invokestatic  #147                // Method kotlin/text/StringsKt.isBlank:(Ljava/lang/CharSequence;)Z
      83: ifne          46
      86: aload         4
      88: aload         7
      90: invokeinterface #151,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
      95: pop
      96: goto          46
      99: aload         4
     101: checkcast     #75                 // class java/util/List
     104: nop
     105: checkcast     #67                 // class java/lang/Iterable
     108: astore_1
     109: nop
     110: iconst_0
     111: istore_2
     112: aload_1
     113: astore_3
     114: new           #133                // class java/util/ArrayList
     117: dup
     118: aload_1
     119: bipush        10
     121: invokestatic  #155                // Method kotlin/collections/CollectionsKt.collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
     124: invokespecial #158                // Method java/util/ArrayList."<init>":(I)V
     127: checkcast     #136                // class java/util/Collection
     130: astore        4
     132: iconst_0
     133: istore        5
     135: aload_3
     136: invokeinterface #137,  1          // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     141: astore        6
     143: aload         6
     145: invokeinterface #85,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     150: ifeq          193
     153: aload         6
     155: invokeinterface #89,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     160: astore        7
     162: aload         4
     164: aload         7
     166: checkcast     #139                // class java/lang/String
     169: astore        8
     171: astore        10
     173: iconst_0
     174: istore        9
     176: aload         8
     178: invokestatic  #162                // Method toPacket:(Ljava/lang/String;)Lday13/Packet;
     181: aload         10
     183: swap
     184: invokeinterface #151,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     189: pop
     190: goto          143
     193: aload         4
     195: checkcast     #75                 // class java/util/List
     198: nop
     199: areturn

  private static final day13.Packet toPacket(java.lang.String);
    Code:
       0: new           #183                // class day13/ParserState
       3: dup
       4: aload_0
       5: iconst_0
       6: iconst_2
       7: aconst_null
       8: invokespecial #186                // Method day13/ParserState."<init>":(Ljava/lang/String;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
      11: invokestatic  #190                // Method packet:(Lday13/ParserState;)Lday13/Packet;
      14: areturn

  private static final char peek(day13.ParserState);
    Code:
       0: aload_0
       1: invokevirtual #196                // Method day13/ParserState.getSource:()Ljava/lang/String;
       4: aload_0
       5: invokevirtual #199                // Method day13/ParserState.getCurrent:()I
       8: invokevirtual #203                // Method java/lang/String.charAt:(I)C
      11: ireturn

  private static final char next(day13.ParserState);
    Code:
       0: aload_0
       1: invokevirtual #196                // Method day13/ParserState.getSource:()Ljava/lang/String;
       4: aload_0
       5: invokevirtual #199                // Method day13/ParserState.getCurrent:()I
       8: istore_1
       9: aload_0
      10: iload_1
      11: iconst_1
      12: iadd
      13: invokevirtual #208                // Method day13/ParserState.setCurrent:(I)V
      16: iload_1
      17: invokevirtual #203                // Method java/lang/String.charAt:(I)C
      20: ireturn

  private static final day13.Packet packet(day13.ParserState);
    Code:
       0: aload_0
       1: invokestatic  #211                // Method peek:(Lday13/ParserState;)C
       4: bipush        91
       6: if_icmpne     19
       9: aload_0
      10: invokestatic  #215                // Method list:(Lday13/ParserState;)Lday13/Packet$List;
      13: checkcast     #96                 // class day13/Packet
      16: goto          26
      19: aload_0
      20: invokestatic  #219                // Method integer:(Lday13/ParserState;)Lday13/Packet$Integer;
      23: checkcast     #96                 // class day13/Packet
      26: areturn

  private static final day13.Packet$List list(day13.ParserState);
    Code:
       0: new           #133                // class java/util/ArrayList
       3: dup
       4: invokespecial #134                // Method java/util/ArrayList."<init>":()V
       7: checkcast     #75                 // class java/util/List
      10: astore_1
      11: aload_0
      12: invokestatic  #222                // Method next:(Lday13/ParserState;)C
      15: pop
      16: aload_0
      17: invokestatic  #211                // Method peek:(Lday13/ParserState;)C
      20: bipush        93
      22: if_icmpeq     53
      25: aload_1
      26: aload_0
      27: invokestatic  #190                // Method packet:(Lday13/ParserState;)Lday13/Packet;
      30: invokeinterface #223,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      35: pop
      36: aload_0
      37: invokestatic  #211                // Method peek:(Lday13/ParserState;)C
      40: bipush        44
      42: if_icmpne     16
      45: aload_0
      46: invokestatic  #222                // Method next:(Lday13/ParserState;)C
      49: pop
      50: goto          16
      53: aload_0
      54: invokestatic  #222                // Method next:(Lday13/ParserState;)C
      57: pop
      58: new           #225                // class day13/Packet$List
      61: dup
      62: aload_1
      63: invokespecial #228                // Method day13/Packet$List."<init>":(Ljava/util/List;)V
      66: areturn

  private static final day13.Packet$Integer integer(day13.ParserState);
    Code:
       0: iconst_0
       1: istore_1
       2: aload_0
       3: invokestatic  #211                // Method peek:(Lday13/ParserState;)C
       6: invokestatic  #236                // Method java/lang/Character.isDigit:(C)Z
       9: ifeq          28
      12: iload_1
      13: bipush        10
      15: imul
      16: aload_0
      17: invokestatic  #222                // Method next:(Lday13/ParserState;)C
      20: invokestatic  #242                // Method kotlin/text/CharsKt.digitToInt:(C)I
      23: iadd
      24: istore_1
      25: goto          2
      28: new           #244                // class day13/Packet$Integer
      31: dup
      32: iload_1
      33: invokespecial #245                // Method day13/Packet$Integer."<init>":(I)V
      36: areturn

  private static final int part1(java.util.List<? extends day13.Packet>);
    Code:
       0: new           #133                // class java/util/ArrayList
       3: dup
       4: invokespecial #134                // Method java/util/ArrayList."<init>":()V
       7: checkcast     #75                 // class java/util/List
      10: astore_1
      11: aload_0
      12: checkcast     #67                 // class java/lang/Iterable
      15: iconst_2
      16: invokestatic  #251                // Method kotlin/collections/CollectionsKt.chunked:(Ljava/lang/Iterable;I)Ljava/util/List;
      19: checkcast     #67                 // class java/lang/Iterable
      22: invokeinterface #137,  1          // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      27: astore_2
      28: iconst_0
      29: istore_3
      30: aload_2
      31: invokeinterface #85,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      36: ifeq          110
      39: iload_3
      40: istore        4
      42: iload_3
      43: iconst_1
      44: iadd
      45: istore_3
      46: aload_2
      47: invokeinterface #89,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      52: checkcast     #75                 // class java/util/List
      55: astore        5
      57: aload         5
      59: iconst_0
      60: invokeinterface #255,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      65: checkcast     #96                 // class day13/Packet
      68: astore        6
      70: aload         5
      72: iconst_1
      73: invokeinterface #255,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      78: checkcast     #96                 // class day13/Packet
      81: astore        7
      83: aload         6
      85: aload         7
      87: invokevirtual #103                // Method day13/Packet.compareTo:(Ljava/lang/Object;)I
      90: ifge          30
      93: aload_1
      94: iload         4
      96: iconst_1
      97: iadd
      98: invokestatic  #261                // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
     101: invokeinterface #223,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
     106: pop
     107: goto          30
     110: aload_1
     111: checkcast     #67                 // class java/lang/Iterable
     114: invokestatic  #265                // Method kotlin/collections/CollectionsKt.sumOfInt:(Ljava/lang/Iterable;)I
     117: ireturn

  private static final int part2(java.util.List<? extends day13.Packet>);
    Code:
       0: getstatic     #271                // Field day13/Packet.Companion:Lday13/Packet$Companion;
       3: iconst_1
       4: anewarray     #96                 // class day13/Packet
       7: astore_2
       8: aload_2
       9: iconst_0
      10: getstatic     #271                // Field day13/Packet.Companion:Lday13/Packet$Companion;
      13: iconst_2
      14: invokevirtual #277                // Method day13/Packet$Companion.of:(I)Lday13/Packet$Integer;
      17: aastore
      18: aload_2
      19: invokevirtual #280                // Method day13/Packet$Companion.of:([Lday13/Packet;)Lday13/Packet$List;
      22: astore_1
      23: getstatic     #271                // Field day13/Packet.Companion:Lday13/Packet$Companion;
      26: iconst_1
      27: anewarray     #96                 // class day13/Packet
      30: astore_3
      31: aload_3
      32: iconst_0
      33: getstatic     #271                // Field day13/Packet.Companion:Lday13/Packet$Companion;
      36: bipush        6
      38: invokevirtual #277                // Method day13/Packet$Companion.of:(I)Lday13/Packet$Integer;
      41: aastore
      42: aload_3
      43: invokevirtual #280                // Method day13/Packet$Companion.of:([Lday13/Packet;)Lday13/Packet$List;
      46: astore_2
      47: aload_0
      48: checkcast     #136                // class java/util/Collection
      51: iconst_2
      52: anewarray     #225                // class day13/Packet$List
      55: astore        4
      57: aload         4
      59: iconst_0
      60: aload_1
      61: aastore
      62: aload         4
      64: iconst_1
      65: aload_2
      66: aastore
      67: aload         4
      69: invokestatic  #284                // Method kotlin/collections/CollectionsKt.listOf:([Ljava/lang/Object;)Ljava/util/List;
      72: checkcast     #67                 // class java/lang/Iterable
      75: invokestatic  #288                // Method kotlin/collections/CollectionsKt.plus:(Ljava/util/Collection;Ljava/lang/Iterable;)Ljava/util/List;
      78: checkcast     #67                 // class java/lang/Iterable
      81: invokestatic  #292                // Method kotlin/collections/CollectionsKt.sorted:(Ljava/lang/Iterable;)Ljava/util/List;
      84: astore_3
      85: aload_3
      86: aload_1
      87: invokeinterface #295,  2          // InterfaceMethod java/util/List.indexOf:(Ljava/lang/Object;)I
      92: iconst_1
      93: iadd
      94: aload_3
      95: aload_2
      96: invokeinterface #295,  2          // InterfaceMethod java/util/List.indexOf:(Ljava/lang/Object;)I
     101: iconst_1
     102: iadd
     103: imul
     104: ireturn

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #301                // Method main:()V
       3: return

  public static final int access$compareContents(java.util.List, java.util.List);
    Code:
       0: aload_0
       1: aload_1
       2: invokestatic  #306                // Method compareContents:(Ljava/util/List;Ljava/util/List;)I
       5: ireturn
}