//! By convention, root.zig is the root source file when making a library.
const std = @import("std");

pub const platform = @import("platform.zig");

const TILE_SIZE = 32;

const V2 = struct {
    x: f32,
    y: f32,

    fn add(self: V2, other: V2) V2 {
        return .{
            .x = self.x + other.x,
            .y = self.y + other.y,
        };
    }

    fn sub(self: V2, other: V2) V2 {
        return .{
            .x = self.x - other.x,
            .y = self.y - other.y,
        };
    }
};

const Camera = struct {
    pos: V2,
    offset: V2,
};

const Brick = struct {
    pos: V2,
    tiles: [4][4]u32,
};

const State = struct {
    frame_count: i32,
    mouse_pos: V2,
    camera: Camera,
    brick: Brick,
};

fn pointInsideRect(p: V2, rect_start: V2, rect_size: V2) bool {
    var result: bool = false;

    const rect_end: V2 = rect_start.add(rect_size);

    const inside_x = rect_start.x < p.x and p.x < rect_end.x;
    const inside_y = rect_start.y < p.y and p.y < rect_end.y;

    result = inside_x and inside_y;

    return result;
}

fn fillRect(
    buffer: platform.OffscreenBuffer,
    pos_x: i32,
    pos_y: i32,
    width: i32,
    height: i32,
    color: u32,
) void {
    var pixels: []u32 = @ptrCast(@alignCast(buffer.data));

    var start_x: i32 = pos_x;
    var start_y: i32 = pos_y;
    var end_x: i32 = pos_x + width;
    var end_y: i32 = pos_y + height;

    if (start_x < 0) start_x = 0;
    if (start_y < 0) start_y = 0;
    if (buffer.width < end_x) end_x = buffer.width;
    if (buffer.height < end_y) end_y = buffer.height;

    for (@intCast(start_y) .. @intCast(end_y)) |y| {
        for (@intCast(start_x) .. @intCast(end_x)) |x| {
            const idx: usize = y * @as(usize, @intCast(buffer.width)) + x;

            pixels[idx] = color;
        }
    }
}

fn fillSquare(
    buffer: platform.OffscreenBuffer,
    x: i32,
    y: i32,
    size: i32,
    color: u32,
) void {
    fillRect(buffer, x, y, size, size, color);
}

pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBuffer, input: platform.AppInput) void {
    var state: *State = @ptrCast(@alignCast(memory.permanent_storage));

    const mid = V2{ .x = @floatFromInt(@divTrunc(buffer.width, 2)), .y = @floatFromInt(@divTrunc(buffer.height, 2)), };
    if (!memory.initialized) {
        state.camera.offset = mid;

        state.brick = Brick{
            .pos = V2{ .x = 50, .y = 50},
            .tiles = [4][4]u32{
                .{1, 1, 1, 1},
                .{1, 0, 0, 0},
                .{0, 0, 0, 0},
                .{0, 0, 0, 0},
            },
        };

        memory.initialized = true;
    }

    // Update
    const mouse_pos = V2{ .x = @floatCast(input.mouse_x), .y = @floatCast(input.mouse_y) };

    const tile_size = V2{
        .x = TILE_SIZE,
        .y = TILE_SIZE,
    };

    var mouse_on_brick = false;
    for (state.brick.tiles, 0..) |row, i| {
        for (row, 0..) |tile, j| {
            if (tile == 1) {
                const tile_pos = V2{
                    .x = @floatFromInt(j * TILE_SIZE),
                    .y = @floatFromInt(i * TILE_SIZE),
                };
                const pos = tile_pos.add(state.brick.pos);

                const camera_pos = pos.add(state.camera.pos);
                if (pointInsideRect(state.mouse_pos, camera_pos, tile_size)) {
                    if (input.mouse_left_down) {
                        const diff = mouse_pos.sub(state.mouse_pos);
                        state.brick.pos = state.brick.pos.add(diff);
                        mouse_on_brick = true;
                    }
                }
            }
        }
    }

    if (input.mouse_left_down and !mouse_on_brick) {
        const diff = mouse_pos.sub(state.mouse_pos);
        state.camera.pos = state.camera.pos.add(diff);
    }


    state.mouse_pos = mouse_pos;

    // Render
    fillRect(buffer, 0, 0, buffer.width, buffer.height, 0xFFFFFFFF);

    const board_pos = mid;
    for (0..7) |i| {
        for (0..7) |j| {
            if (i == 0 and j == 6) continue;
            if (i == 1 and j == 6) continue;
            if (i == 6 and j == 3) continue;
            if (i == 6 and j == 4) continue;
            if (i == 6 and j == 5) continue;
            if (i == 6 and j == 6) continue;

            const tile_pos = V2{
                .x = @floatFromInt(j * TILE_SIZE),
                .y = @floatFromInt(i * TILE_SIZE),
            };
            const camera_pos = board_pos.add(tile_pos).add(state.camera.pos);
            fillSquare(buffer, @intFromFloat(camera_pos.x), @intFromFloat(camera_pos.y), 30, 0xFF000000);
        }
    }

    for (state.brick.tiles, 0..) |row, i| {
        for (row, 0..) |tile, j| {
            if (tile == 1) {
                const pos = V2{
                    .x = @floatFromInt(j * TILE_SIZE),
                    .y = @floatFromInt(i * TILE_SIZE),
                };
                const camera_pos = state.brick.pos.add(pos).add(state.camera.pos);
                fillSquare(buffer, @intFromFloat(camera_pos.x), @intFromFloat(camera_pos.y), 30, 0xFFFF0000);
            }
        }
    }

    state.frame_count += 1;
}

