package eu.midnightdust.yaytris.game;

import eu.midnightdust.yaytris.Tetris;
import eu.midnightdust.yaytris.util.SoundEffect;
import eu.midnightdust.yaytris.util.Vec2i;

import java.awt.*;

public class Tetromino {
    private final TetrominoShape shape;
    private int[][] collision;
    private Vec2i centerPos;
    private int fallLength = 0;

    public Tetromino(TetrominoShape shape) {
        this.shape = shape;
        this.collision = shape.boundary;
        this.centerPos = Vec2i.of(Tetris.getSpace().getMapWidth()/2-1, -1);
    }

    public boolean fall() {
        Vec2i newPos = centerPos.offset(Vec2i.of(0, 1));
        if (collidesVertically(newPos)) {
            SoundEffect.BOOP.play();
            int[] affectedLines = new int[this.collision.length];
            int line = centerPos.getY();
            for (int i = 0; i < this.collision.length; i++) {
                affectedLines[i] = line;
                line++;
            }
            Tetris.getSpace().onLinesChanged(this, affectedLines);
            Tetris.getSpace().increaseScore(20-fallLength);
            return false;
        }
        fallLength += 1;
        centerPos = newPos;
        return true;
    }

    public void move(int xOffset) {
        Vec2i newPos = centerPos.offset(Vec2i.of(xOffset, 0));
        if (collidesHorizontally(newPos, xOffset)) {
            return;
        }
        centerPos = newPos;
    }

    private boolean collidesVertically(Vec2i newPos) {
        if (Tetris.getSpace() == null) return false;

        boolean collides = newPos.getY() + this.collision.length > Tetris.getSpace().getMapHeight(); // Bottom check

        if (!collides) { // Check for other tetrominos
            for (int i = 0; i < collision[0].length; i++) {
                int maxCollisionY = collision.length - 1;
                while (collision[maxCollisionY][i] == 0) maxCollisionY--; // Figure out the collision box's bounding

                if (newPos.getY()+maxCollisionY < 0 || newPos.getY()+maxCollisionY >= Tetris.getSpace().getMapHeight() || newPos.getX() + i >= Tetris.getSpace().getMapWidth()) continue;
                collides |= Tetris.getSpace().getGameMap()[newPos.getY() + maxCollisionY][newPos.getX() + i] != null;
            }
        }
        return collides;
    }

    private boolean collidesHorizontally(Vec2i newPos, int xOffset) {
        if (Tetris.getSpace() == null) return false;

        boolean collides = newPos.getX() < 0 || newPos.getX() + collision[0].length > Tetris.getSpace().getMapWidth(); // Side check
        if (!collides) { // Check for other tetrominos
            for (int i = 0; i < collision.length; i++) {
                int maxCollisionX = xOffset >= 0 ? collision[i].length - 1 : 0;
                while (collision[i][maxCollisionX] == 0) maxCollisionX += xOffset >= 0 ? -1 : 1; // Figure out the collision box's bounding

                if (newPos.getY()+i < 0 || newPos.getY()+i >= Tetris.getSpace().getMapHeight()) continue;
                collides |= Tetris.getSpace().getGameMap()[newPos.getY() + i][newPos.getX() + maxCollisionX] != null;
            }
        }
        return collides;
    }

    public void rotate() {
        int M = collision.length;
        int N = collision[0].length;

        int[][] newCollision = new int[N][M];
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                newCollision[j][M-i-1] = collision[i][j];
            }
        }
        int[][] prevCollision = this.collision;
        this.collision = newCollision;
        int offset = 0;
        for (int i = 0; i < 4; i++) {
            if (collidesHorizontally(this.centerPos.offset(Vec2i.of(offset, 0)), -i)) offset = -i;
        }
        if (collidesVertically(this.centerPos.offset(Vec2i.of(offset, 0))) || collidesHorizontally(this.centerPos.offset(Vec2i.of(offset, 0)), offset)) this.collision = prevCollision;
        else this.centerPos = centerPos.offset(Vec2i.of(offset, 0));
    }

    public Color[] getLine(int line) {
        Color[] l = new Color[Tetris.getSpace().getMapWidth()];
        for (int i = 0; i < l.length; i++) {
            int relY = line - centerPos.getY();
            if (relY >= collision.length || relY < 0) continue;
            int relX = i-centerPos.getX();
            if (relX >= collision[relY].length || relX < 0) continue;

            if (collision[relY][relX] != 0) l[i] = shape.color;
        }
        return l;
    }

    public void fallToBottom() {
        while (true) {
            if (!fall()) break;
        }
    }
}
