const SDL = @import("sdl2");
const std = @import("std");

const Color = @import("color.zig");
const Grid = @import("grid.zig");

const Self = @This();

pub const Rot = enum {
    right,
    left,
};

pub const Type = enum {
    o,
    i,
    l,
    j,
    s,
    z,
    t,
};

const ncols = 4;
const nrows = 4;

// Positions relative to a grid
row: i32,
col: i32,

// 4x4 grid indicating piece form
structure: [4][4]bool,

// Should this be dropped and become part of the grid (flag)
dropped: bool = false,
swapped: bool = false,
piece_type: Type,
color: SDL.SDL_Color,

pub fn init(piece_type: Type) Self {
    return Self{
        .row = Grid.buffer - 3,
        .col = (Grid.ncolumns / 2) - (ncols / 2),
        .piece_type = piece_type,

        .structure = switch (piece_type) {
            Type.o => .{
                .{ false, true, true, false },
                .{ false, true, true, false },
                .{ false, false, false, false },
                .{ false, false, false, false },
            },
            Type.i => .{
                .{ false, false, false, false },
                .{ true, true, true, true },
                .{ false, false, false, false },
                .{ false, false, false, false },
            },
            Type.l => .{
                .{ false, false, true, false },
                .{ true, true, true, false },
                .{ false, false, false, false },
                .{ false, false, false, false },
            },
            Type.j => .{
                .{ true, false, false, false },
                .{ true, true, true, false },
                .{ false, false, false, false },
                .{ false, false, false, false },
            },
            Type.s => .{
                .{ false, true, true, false },
                .{ true, true, false, false },
                .{ false, false, false, false },
                .{ false, false, false, false },
            },
            Type.z => .{
                .{ true, true, false, false },
                .{ false, true, true, false },
                .{ false, false, false, false },
                .{ false, false, false, false },
            },
            Type.t => .{
                .{ false, true, false, false },
                .{ true, true, true, false },
                .{ false, false, false, false },
                .{ false, false, false, false },
            },
        },

        .color = switch (piece_type) {
            Type.o => Color.yellow,
            Type.i => Color.cyan,
            Type.l => Color.orange,
            Type.j => Color.blue,
            Type.s => Color.green,
            Type.z => Color.red,
            Type.t => Color.purple,
        },
    };
}

pub fn rotate(self: Self, dir: Rot) Self {
    if (self.piece_type == Type.o) {
        return self;
    }

    var new_piece = self;

    if (self.piece_type == Type.i) {
        const sequences = .{ .{ .{ 1, 0 }, .{ 0, 2 }, .{ 2, 3 }, .{ 3, 1 } }, .{ .{ 2, 0 }, .{ 0, 1 }, .{ 1, 3 }, .{ 3, 2 } }, .{ .{ 1, 1 }, .{ 1, 2 }, .{ 2, 2 }, .{ 2, 1 } } };

        inline for (sequences) |seq| {
            inline for (seq) |_, i| {
                if (dir == Rot.right) {
                    const refi = @mod((@intCast(i32, i) - 1), 4);
                    new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]];
                } else {
                    const refi = @mod((@intCast(i32, i) + 1), 4);
                    new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]];
                }
            }
        }
    } else {
        const sequences = .{ .{ .{ 0, 0 }, .{ 0, 2 }, .{ 2, 2 }, .{ 2, 0 } }, .{ .{ 0, 1 }, .{ 1, 2 }, .{ 2, 1 }, .{ 1, 0 } } };

        inline for (sequences) |seq| {
            inline for (seq) |_, i| {
                if (dir == Rot.right) {
                    const refi = @mod((@intCast(i32, i) - 1), 4);
                    new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]];
                } else {
                    const refi = @mod((@intCast(i32, i) + 1), 4);
                    new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]];
                }
            }
        }
    }

    //[0,0][0,1][0,2]
    //[1,0][1,1][1,2]
    //[2,0][2,1][2,2]

    return new_piece;
}

pub fn render(self: Self, renderer: *SDL.SDL_Renderer, cell_size: i32, pos_x: i32, pos_y: i32) void {
    var rect = SDL.SDL_Rect{
        .x = pos_x,
        .y = pos_y,
        .w = cell_size,
        .h = cell_size,
    };

    for (self.structure) |_, y| {
        for (self.structure[y]) |_, x| {
            // We don't want to paint void cells
            if (self.structure[y][x] == false) {
                continue;
            }

            var r = self.color.r;
            var g = self.color.g;
            var b = self.color.b;
            var a = self.color.a;

            rect.x = pos_x + (@intCast(i32, x) + self.col) * cell_size;
            rect.y = pos_y + (@intCast(i32, y) + self.row) * cell_size;

            _ = SDL.SDL_SetRenderDrawColor(renderer, r, g, b, a);
            _ = SDL.SDL_RenderFillRect(renderer, &rect);
            _ = SDL.SDL_SetRenderDrawColor(renderer, r -| 30, g -| 30, b -| 30, a +| 30);
            _ = SDL.SDL_RenderDrawRect(renderer, &rect);
        }
    }
}
