const std = @import("std");
const Grid = @import("grid.zig");
const Piece = @import("piece.zig");

pub fn moveRight(grid: Grid, piece: Piece) Piece {
    var new_piece = piece;
    new_piece.col += 1;
    if (checkCollision(grid, new_piece)) {
        return piece;
    } else {
        return new_piece;
    }
}

pub fn moveLeft(grid: Grid, piece: Piece) Piece {
    var new_piece = piece;
    new_piece.col -= 1;
    if (checkCollision(grid, new_piece)) {
        return piece;
    } else {
        return new_piece;
    }
}

pub fn moveDown(grid: Grid, piece: Piece) Piece {
    var new_piece = piece;
    new_piece.row += 1;
    if (checkCollision(grid, new_piece)) {
        new_piece.row -= 1;
        new_piece.dropped = true;
        return new_piece;
    } else {
        return new_piece;
    }
}

pub fn hardDrop(grid: Grid, piece: Piece) Piece {
    var new_piece = piece;
    while (!new_piece.dropped) {
        new_piece = moveDown(grid, new_piece);
    }
    return new_piece;
}

pub fn drop(grid: Grid, piece: Piece) Grid {
    var new_grid = grid;

    for (piece.structure) |_, y| {
        for (piece.structure[y]) |_, x| {
            if (piece.structure[y][x]) {
                new_grid.cells[@intCast(usize, piece.row + @intCast(i32, y))][@intCast(usize, piece.col + @intCast(i32, x))].free = false;
                new_grid.cells[@intCast(usize, piece.row + @intCast(i32, y))][@intCast(usize, piece.col + @intCast(i32, x))].color = piece.color;
            }
        }
    }

    return new_grid;
}

pub fn shadow(grid: Grid, piece: Piece) Piece {
    var new_shadow = hardDrop(grid, piece);
    return new_shadow;
}

fn checkCollision(grid: Grid, piece: Piece) bool {
    for (piece.structure) |_, y| {
        for (piece.structure[y]) |_, x| {
            if (piece.structure[y][x] and ((@intCast(i32, x) + piece.col > Grid.ncolumns - 1) or (@intCast(i32, x) + piece.col < 0) or (@intCast(i32, y) + piece.row > Grid.nrows - 1) or (@intCast(i32, y) + piece.row < 0) or (!grid.cells[@intCast(usize, piece.row + @intCast(i32, y))][@intCast(usize, piece.col + @intCast(i32, x))].free))) {
                return true;
            }
        }
    }
    return false;
}

pub fn rotateLeft(grid: Grid, piece: Piece) Piece {
    var new_piece = piece.rotate(Piece.Rot.left);
    new_piece = kick(grid, new_piece, piece);
    std.debug.print("{}\n", .{new_piece.rot_stage});
    return new_piece;
}

pub fn rotateRight(grid: Grid, piece: Piece) Piece {
    var new_piece = piece.rotate(Piece.Rot.right);
    new_piece = kick(grid, new_piece, piece);
    std.debug.print("{}\n", .{new_piece.rot_stage});
    return new_piece;
}

pub fn kick(grid: Grid, piece: Piece, prev_piece: Piece) Piece {
    // O piece does not rotate
    if (piece.piece_type == Piece.Type.o) return piece;
    // Get the rotation stage for appropiate checks
    const prev_stage = prev_piece.rot_stage;
    const next_stage = piece.rot_stage;
    var new_piece = piece;

    // Test 1
    if (!checkCollision(grid, new_piece)) return new_piece;

    var offsets: [4][2]i8 = undefined;

    if (piece.piece_type == Piece.Type.i) {
        if ((prev_stage == Piece.RotStage.init and next_stage == Piece.RotStage.right) or (prev_stage == Piece.RotStage.left and next_stage == Piece.RotStage.flip)) {
            offsets = .{ .{ -2, 0 }, .{ 1, 0 }, .{ -2, 1 }, .{ 1, -2 } };
        } else if ((prev_stage == Piece.RotStage.right and next_stage == Piece.RotStage.init) or (prev_stage == Piece.RotStage.flip and next_stage == Piece.RotStage.left)) {
            offsets = .{ .{ 2, 0 }, .{ -1, 0 }, .{ 2, -1 }, .{ -1, 2 } };
        } else if ((prev_stage == Piece.RotStage.right and next_stage == Piece.RotStage.flip) or (prev_stage == Piece.RotStage.init and next_stage == Piece.RotStage.left)) {
            offsets = .{ .{ -1, 0 }, .{ 2, 0 }, .{ -1, -2 }, .{ 2, 1 } };
        } else {
            offsets = .{ .{ 1, 0 }, .{ -2, 0 }, .{ 1, 2 }, .{ -2, -1 } };
        }
    } else {
        if ((prev_stage == Piece.RotStage.init and next_stage == Piece.RotStage.right) or (prev_stage == Piece.RotStage.flip and next_stage == Piece.RotStage.right)) {
            offsets = .{ .{ -1, 0 }, .{ -1, -1 }, .{ 0, 2 }, .{ -1, 2 } };
        } else if ((prev_stage == Piece.RotStage.right and next_stage == Piece.RotStage.init) or (prev_stage == Piece.RotStage.right and next_stage == Piece.RotStage.flip)) {
            offsets = .{ .{ 1, 0 }, .{ 1, 1 }, .{ 0, -2 }, .{ 1, -2 } };
        } else if ((prev_stage == Piece.RotStage.flip and next_stage == Piece.RotStage.left) or (prev_stage == Piece.RotStage.init and next_stage == Piece.RotStage.left)) {
            offsets = .{ .{ 1, 0 }, .{ 1, -1 }, .{ 0, 2 }, .{ 1, 2 } };
        } else {
            offsets = .{ .{ -1, 0 }, .{ -1, 1 }, .{ 0, -2 }, .{ -1, -2 } };
        }
    }

    // Try offsets

    for (offsets) |offset| {
        new_piece.col += offset[0];
        new_piece.row += offset[1];
        if (!checkCollision(grid, new_piece)) return new_piece;
        new_piece.col -= offset[0];
        new_piece.row -= offset[1];
    }

    return prev_piece;
}
