const std = @import("std");
const fetch = @import("fetch");

const Str = []const u8;

const opts = fetch.Options{
    .year = 2024,
    .days = 1,
};

const aoc2024 = struct {
    const day01 = @import("day01.zig");
};

test {
    _ = @import("day01.zig");
}

pub fn main() anyerror!void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    if (!fetch.checkInputs(opts.days)) {
        try fetch.fetchInputs(allocator, opts);
    }
    try solveAll(allocator);
}

fn solveAll(allocator: std.mem.Allocator) anyerror!void {
    var timer = try std.time.Timer.start();
    var time_sum: usize = 0;

    var buffer = std.io.bufferedWriter(std.io.getStdOut().writer());
    const stdout = buffer.writer();

    try stdout.print("{s:<8}{s:>45}{s:>20}\n", .{ "Day", "Result", "Time" });

    comptime var day: u5 = 0;
    inline while (day < opts.days) : (day += 1) {
        comptime var buf: [5]u8 = undefined;
        const day_str = comptime try std.fmt.bufPrint(&buf, "day{d:0>2}", .{day + 1});

        timer.reset();
        const first_res = @field(aoc2024, day_str).first(allocator) catch unreachable;
        const first_time = timer.read() / std.time.ns_per_us;
        time_sum += first_time;

        {
            const runtime_buffer = buf; // comptime -> runtime hack

            switch (@TypeOf(first_res)) {
                Str => try stdout.print("{s}a: {s:>45}{d:>20} us\n", .{ runtime_buffer, first_res, first_time }),
                usize, isize => try stdout.print("{s}a: {d:>45}{d:>20} us\n", .{ runtime_buffer, first_res, first_time }),
                else => try stdout.print("{s}a: {s:>45}{d:>20} us\n", .{ runtime_buffer, "", first_time }),
            }
        }

        if (day < 24) {
            timer.reset();
            const second_res = @field(aoc2024, day_str).second(allocator) catch unreachable;
            const second_time = timer.read() / std.time.ns_per_us;
            time_sum += second_time;

            {
                const runtime_buffer = buf; // comptime -> runtime hack

                switch (@TypeOf(second_res)) {
                    Str => try stdout.print("{s}b: {s:>45}{d:>20} us\n", .{ runtime_buffer, second_res, second_time }),
                    usize, isize => try stdout.print("{s}b: {d:>45}{d:>20} us\n", .{ runtime_buffer, second_res, second_time }),
                    else => try stdout.print("{s}b: {s:>45}{d:>20} us\n", .{ runtime_buffer, "", second_time }),
                }
            }
        }
    }

    try stdout.print("Total time: {d} ms\n", .{time_sum / std.time.us_per_ms});

    try buffer.flush();
}