debug_it :: false;

solve_day13 :: (test: bool) {
    contents := read_entire_file(ifx test then "inputs/day13_test.txt" else "inputs/day13.txt");
    sections := split(contents, "\n\n");

    part1 := 0;

    signal_lists: [..]Signal;

    for section: sections {
        section_lines := split(section, "\n");

        parse_info: Parse_Info = .{
            data = section_lines[0],
            cursor = 0,
        };
        list\_left := parse_list(*parse_info);
        parse_info = .{
            data = section_lines[1],
            cursor = 0,
        };
        list_right := parse_list(*parse_info);

        array_add(*signal_lists, list_left);
        array_add(*signal_lists, list_right);

        if is_right_ordered(list_left, list_right) < 0 {
            if debug_it  print("Left side is smaller\n\n");
            if debug_it  print("Adding %\n", it_index + 1);
            part1 += it_index + 1;
        } else {
            if debug_it  print("Right side is smaller\n\n");
        }
    }

    integer_divider := Signal.{
        signal_type = .INTEGER,
        integer = 2,
    };

    divider_1: Signal = .{
        signal_type = .LIST,
    };
    divider_1_2: Signal = .{
        signal_type = .LIST,
    };
    array_add(*divider_1_2.list, integer_divider);
    array_add(*divider_1.list, divider_1_2);

    divider_2: Signal = .{
        signal_type = .LIST,
    };
    divider_2_2: Signal = .{
        signal_type = .LIST,
    };

    defer {
        array_free(divider_1_2.list);
        array_free(divider_2_2.list);
        array_free(divider_1.list);
        array_free(divider_2.list);
    }

    integer_divider.integer = 6;
    array_add(*divider_2_2.list, integer_divider);
    array_add(*divider_2.list, divider_2_2);

    array_add(*signal_lists, divider_1);
    array_add(*signal_lists, divider_2);

    quick_sort(signal_lists, is_right_ordered);

    print("Part 1: %\n", part1);

    part2 := 1;

    for signal_lists {
        if it.list.count == 1 && it.list[0].list.count == 1 && (it.list[0].list[0].integer == 2 || it.list[0].list[0].integer == 6) {
            part2 *= (it_index + 1);
        }
    }

    print("Part 2: %\n", part2);
}

is_right_ordered :: (left: Signal, right: Signal) -> s64 {
    if (left.signal_type == .LIST    && right.signal_type == .INTEGER) ||
       (left.signal_type == .INTEGER && right.signal_type == .LIST) {
        signal := Signal.{
            signal_type = .LIST,
        };
        defer array_free(signal.list);


        if left.signal_type == .INTEGER {
            array_add(*signal.list, left);
            if debug_it  print("Mixed type; convert left side to [%] and retry comparison\n", left.integer);
            if debug_it  print("Compare ");
            if debug_it  print_list(signal);
            if debug_it  print(" vs ");
            if debug_it  print_list(right);
            if debug_it  print("\n");
            return is_right_ordered(signal, right);
        } else {
            array_add(*signal.list, right);
            if debug_it  print("Mixed type; convert right side to [%] and retry comparison\n", right.integer);
            if debug_it  print("Compare ");
            if debug_it  print_list(left);
            if debug_it  print(" vs ");
            if debug_it  print_list(signal);
            if debug_it  print("\n");
            return is_right_ordered(left, signal);
        }
    } else if left.signal_type == .INTEGER {
        if debug_it  print("Compare % vs %\n", left.integer, right.integer);
        return left.integer - right.integer;
    } else {
        cursor := 0;

        if debug_it  print("Compare ");
        if debug_it  print_list(left);
        if debug_it  print(" vs ");
        if debug_it  print_list(right);
        if debug_it  print("\n");

        while cursor < left.list.count {
            if right.list.count <= cursor  return 1;

            res := is_right_ordered(left.list[cursor], right.list[cursor]);

            if res != 0  return res;

            cursor += 1;
        }

        if left.list.count == right.list.count  return 0;

        return -1;
    }

    return 0;
}

parse_list :: (parse_info: *Parse_Info) -> Signal {
    parse_info.cursor += 1;
    list: Signal;
    list.signal_type = .LIST;
    while true {
        if is_digit(parse_info.data[parse_info.cursor]) {
            array_add(*list.list, parse_integer(parse_info));
            continue;
        } else if parse_info.data[parse_info.cursor] == #char "," {
            parse_info.cursor += 1;
            continue;
        } else if parse_info.data[parse_info.cursor] == #char "[" {
            array_add(*list.list, parse_list(parse_info));
            continue;
        } else if parse_info.data[parse_info.cursor] == #char "]" {
            parse_info.cursor += 1;
            break;
        } else {
            assert(false, "Bad character: %", parse_info.data[parse_info.cursor]);
        }
    }

    return list;
}

parse_integer :: (parse_info: *Parse_Info) -> Signal {
    start := parse_info.cursor;
    while is_digit(parse_info.data[parse_info.cursor]) {
        parse_info.cursor += 1;
    }

    return .{
        signal_type = .INTEGER,
        integer = string_to_int(slice(parse_info.data, start, parse_info.cursor - start), 10, s64),
    };
}

print_list :: (list: Signal) {
    print("[");

    for list.list {
        if it.signal_type == .LIST {
            print_list(it);
        } else if it.signal_type == .INTEGER {
            print("%", it.integer);
        } else {
            assert(false, "Unknown signal type '%'", it.signal_type);
        }

        if it_index != list.list.count - 1  print(",");
    }

    print("]");
}

Signal_Type :: enum {
    INTEGER :: 0;
    LIST    :: 1;
}

Signal :: struct {
    signal_type: Signal_Type;
    integer: s64;
    list: [..]Signal;
}

Parse_Info :: struct {
    cursor: s64;
    data: string;
}