const std = @import("std");

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

const grid_rows = 137;
const grid_cols = 139;
const Grid = [grid_rows][grid_cols]u8;

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

    try std.testing.expectEqual(@as(usize, 432), ret);

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

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

    var grid = parseInput();

    var steps: usize = 0;
    while (true) : (steps += 1) {
        // std.debug.print("\nstep: {d}\n", .{steps});
        // drawGrid(&grid);
        const e = moveEast(&grid);
        const s = moveSouth(&grid);

        if (!e and !s) {
            break;
        }
    }

    return steps + 1;
}

fn moveEast(g: *Grid) bool {
    var ret = false;
    for (g) |row, row_idx| {
        for (row) |ch, col_idx| {
            if (ch == '>') { // must be captured ch, not g[row][col]
                const right = (col_idx + 1) % grid_cols;
                // This check _must_ use the row, so we can alter the grid
                if (row[right] == '.') {
                    ret = true;
                    g[row_idx][col_idx] = '.';
                    g[row_idx][right] = '>';
                }
            }
        }
    }
    return ret;
}

fn moveSouth(g: *Grid) bool {
    var ret = false;
    var col_idx: usize = 0;
    while (col_idx < grid_cols) : (col_idx += 1) {
        // fill in the current state to col to avoid snowballing
        // in moveEast it is already done with payload capturing
        var fill_idx: usize = 0;
        var col: [grid_rows]u8 = undefined;
        while (fill_idx < grid_rows) : (fill_idx += 1) {
            col[fill_idx] = g[fill_idx][col_idx];
        }

        for (col) |ch, row_idx| {
            if (ch == 'v') { // using ch instead of g[row][col]
                const down = (row_idx + 1) % grid_rows;
                if (col[down] == '.') { // using col[row], not g[row][col]
                    ret = true;
                    g[row_idx][col_idx] = '.';
                    g[down][col_idx] = 'v';
                }
            }
        }
    }
    return ret;
}

fn drawGrid(g: *Grid) void {
    for (g) |line| {
        std.debug.print("{s}\n", .{line});
    }
}

fn parseInput() Grid {
    const input = @embedFile(path);
    var lines = std.mem.split(u8, input, "\n");

    var g: Grid = undefined;

    var row: usize = 0;
    while (lines.next()) |line| : (row += 1) {
        for (line) |ch, col| {
            std.debug.assert(ch == 'v' or ch == '.' or ch == '>');
            g[row][col] = ch;
        }
    }

    return g;
}

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