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

const Grid = @import("Grid.zig");
const Timer = @import("../Timer.zig");

const color = @import("../color.zig");

const Self = @This();

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

pub const RotStage = enum(i8) {
    init,
    right,
    flip,
    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,

// Rotation Stage
rot_stage: RotStage = RotStage.init,

// Timer to drop the piece
timer_dropped: Timer,
// Should this be dropped and become part of the grid (flag)
dropped: bool = false,
// Has the piece been swapped?
swapped: bool = false,

piece_type: Type,
color: 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,

        .timer_dropped = Timer.init(500),

        .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;

    // The "sequences" indicate square coords and are always the same when rotating, so we make all squares follow said rotation
    // [Inner/Outer][The sequence (0 -> 1 -> 2 -> 3 -> 0...)][Rows, Cols]

    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 } } };

        if (dir == Rot.right) {
            // Cicle through rotation stage
            new_piece.rot_stage = @intToEnum(RotStage, @mod((@enumToInt(new_piece.rot_stage) + 1), 4));
            // Rotate structure CW
            inline for (sequences) |seq| {
                inline for (seq, 0..) |_, i| {
                    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 {
            // Cicle through rotation stage
            new_piece.rot_stage = @intToEnum(RotStage, @mod((@enumToInt(new_piece.rot_stage) - 1), 4));
            // Rotate structure CCW
            inline for (sequences) |seq| {
                inline for (seq, 0..) |_, i| {
                    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 } } };

        if (dir == Rot.right) {
            // Cicle through rotation stage
            new_piece.rot_stage = @intToEnum(RotStage, @mod((@enumToInt(new_piece.rot_stage) + 1), 4));
            // Rotate structure CW
            inline for (sequences) |seq| {
                inline for (seq, 0..) |_, i| {
                    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 {
            // Cicle through rotation stage
            new_piece.rot_stage = @intToEnum(RotStage, @mod((@enumToInt(new_piece.rot_stage) - 1), 4));
            // Rotate structure CCW
            inline for (sequences) |seq| {
                inline for (seq, 0..) |_, i| {
                    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;
}