const std = @import("std");

const path = "data/day10/input.txt";

const Pairs = [4][2]u8{
    .{ '(', ')' },
    .{ '[', ']' },
    .{ '{', '}' },
    .{ '<', '>' },
};

const RetType = u64;

fn parseInput() anyerror!RetType {
    const input = @embedFile(path);
    var lines = std.mem.tokenize(u8, input, "\n");

    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    var pairs = std.ArrayList(u8).init(allocator);
    defer pairs.deinit();

    var scores = std.ArrayList(RetType).init(allocator);
    defer scores.deinit();

    lines: while (lines.next()) |line| {
        pairs.clearRetainingCapacity();
        chars: for (line) |ch| {
            for (Pairs) |p| {
                if (ch == p[1]) { // closing pair
                    const last = pairs.popOrNull();

                    // XXX: zig compiler error!
                    if (last == null) {
                        continue :lines;
                    } else if (last != p[0]) {
                        continue :lines;
                    }
                    continue :chars;
                }
            }
            try pairs.append(ch);
        }
        var score: RetType = 0;

        var i: usize = pairs.items.len;
        while (i > 0) : (i -= 1) {
            score *= 5;
            score += getValue(pairs.items[i - 1]);
        }
        try scores.append(score);
    }

    std.sort.sort(RetType, scores.items, {}, comptime std.sort.asc(RetType));

    return scores.items[scores.items.len / 2];
}

fn getValue(ch: u8) RetType {
    var ret: RetType = undefined;
    switch (ch) {
        '(' => ret = 1,
        '[' => ret = 2,
        '{' => ret = 3,
        '<' => ret = 4,
        else => unreachable,
    }
    return ret;
}

pub fn second(allocator: ?std.mem.Allocator) anyerror!RetType {
    _ = allocator;

    return try parseInput();
}

pub fn main() anyerror!void {
    var timer = try std.time.Timer.start();
    const ret = try second(null);
    const f = timer.lap() / 1000;

    try std.testing.expectEqual(ret, @as(RetType, 3122628974));

    std.debug.print("Day 10b result: {d} \ttime: {d}us\n", .{ ret, f });
}

test "day10b" {
    try std.testing.expectEqual(@as(RetType, 3122628974), try second(std.testing.allocator));
}