const std = @import("std");

const Str = []const u8;

const PATH = "input/day02.txt";

pub fn first(allocator: ?std.mem.Allocator) !usize {
    _ = allocator;

    const input = @embedFile(PATH);
    return try wrapPaper(input);
}

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

    const input = @embedFile(PATH);
    return try wrapRibbon(input);
}

fn wrapPaper(input: Str) !usize {
    var lines = std.mem.tokenize(u8, input, "\n");

    var total: usize = 0;

    while (lines.next()) |line| {
        var lwh = std.mem.tokenize(u8, line, "x");
        const l = try std.fmt.parseUnsigned(usize, lwh.next().?, 10);
        const w = try std.fmt.parseUnsigned(usize, lwh.next().?, 10);
        const h = try std.fmt.parseUnsigned(usize, lwh.next().?, 10);

        const size = 2 * l * w + 2 * w * h + 2 * h * l + std.math.min3(l * w, w * h, h * l);

        total += size;
    }

    return total;
}

fn wrapRibbon(input: Str) !usize {
    var lines = std.mem.tokenize(u8, input, "\n");

    var total: usize = 0;

    while (lines.next()) |line| {
        var lwh = std.mem.tokenize(u8, line, "x");
        const l = try std.fmt.parseUnsigned(usize, lwh.next().?, 10);
        const w = try std.fmt.parseUnsigned(usize, lwh.next().?, 10);
        const h = try std.fmt.parseUnsigned(usize, lwh.next().?, 10);

        const max = std.math.max3(l, w, h);
        const ribbon = blk: {
            var rr: usize = undefined;

            if (l == max) {
                rr = w + w + h + h;
            } else if (w == max) {
                rr = l + l + h + h;
            } else if (h == max) {
                rr = l + l + w + w;
            } else unreachable;

            break :blk rr;
        };

        const bow = l * w * h;

        total += ribbon + bow;
    }

    return total;
}

test "2x3x4" {
    try std.testing.expectEqual(@as(usize, 58), try wrapPaper("2x3x4"));
    try std.testing.expectEqual(@as(usize, 34), try wrapRibbon("2x3x4"));
}

test "1x1x10" {
    try std.testing.expectEqual(@as(usize, 43), try wrapPaper("1x1x10"));
    try std.testing.expectEqual(@as(usize, 14), try wrapRibbon("1x1x10"));
}

test "day02a" {
    try std.testing.expectEqual(@as(usize, 1606483), try first(std.testing.allocator));
}

test "day02b" {
    try std.testing.expectEqual(@as(usize, 3842356), try second(std.testing.allocator));
}