const std = @import("std");

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

const RetType = u12;
const CoordType = isize;

const TargetValue = i9;
const TargetArea = struct {
    xmin: TargetValue,
    xmax: TargetValue,
    ymin: TargetValue,
    ymax: TargetValue,
    fn hit(self: @This(), x: CoordType, y: CoordType) bool {
        if (x >= self.xmin and x <= self.xmax and y >= self.ymin and y <= self.ymax) {
            return true;
        }
        return false;
    }
};

fn parseInput() !TargetArea {
    const input = @embedFile(path);
    const in = std.mem.trimRight(u8, input, "\n");

    var ta: TargetArea = undefined;

    var coords = std.mem.tokenize(u8, in[13..], ", ");

    var xpart = coords.next().?[2..];
    var xcords = std.mem.tokenize(u8, xpart, "..");
    ta.xmin = try std.fmt.parseInt(TargetValue, xcords.next().?, 10);
    ta.xmax = try std.fmt.parseInt(TargetValue, xcords.next().?, 10);

    var ypart = coords.next().?[2..];
    var ycords = std.mem.tokenize(u8, ypart, "..");
    ta.ymin = try std.fmt.parseInt(TargetValue, ycords.next().?, 10);
    ta.ymax = try std.fmt.parseInt(TargetValue, ycords.next().?, 10);

    return ta;
}

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

    // const ta = TargetArea{.xmin = 20, .xmax = 30, .ymin = -10, .ymax = -5};
    const ta = try parseInput();

    var counter: RetType = 0;

    const vxmin = std.math.sqrt(@intCast(usize, ta.xmin));
    const vxmax = ta.xmax;

    const vymin = ta.ymin;
    const vymax = try std.math.absInt(ta.ymin);

    const tmax = try std.math.absInt(@as(CoordType, ta.ymin) * 2);

    var vx: CoordType = vxmin;
    while (vx <= vxmax) : (vx += 1) {
        var vy: CoordType = vymin;
        while (vy <= vymax) : (vy += 1) {
            var x: CoordType = 0;
            var y: CoordType = 0;

            var t: CoordType = 0;
            while (t < tmax) : (t += 1) {
                const dx = velX(vx, t);
                x += dx;
                y += vy - t;

                // this makes it slower :-)
                // if (dx == 0 and x < ta.xmin) break;

                // already missed the target
                if (x > ta.xmax and y < ta.ymin) break;

                if (ta.hit(x, y)) {
                    counter += 1;
                    break;
                }
            }
        }
    }

    return counter;
}

fn velX(vx: CoordType, t: CoordType) CoordType {
    var vel = vx - t;
    if (vel > 0) {
        return vel;
    }
    return 0;
}

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

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

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

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