package eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game;

import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.instance.block.Block;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Tetromino {
    private final Shape shape;
    private final StatelessGame instance;
    private Pos position;
    private int[][] shapeArray;

    public enum Shape {
        I,
        J,
        L,
        O,
        S,
        T,
        Z
    }

    public Tetromino(StatelessGame instance, Shape shape) {
        this.instance = instance;
        this.shape = shape;

        switch (this.shape) {
            case I -> shapeArray = new int[][]{{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}};
            case J -> shapeArray = new int[][]{{1,0,0}, {1,1,1}, {0,0,0}};
            case L -> shapeArray = new int[][]{{0,0,1}, {1,1,1}, {0,0,0}};
            case O -> shapeArray = new int[][]{{1,1}, {1,1}};
            case S -> shapeArray = new int[][]{{0,1,1}, {1,1,0}, {0,0,0}};
            case T -> shapeArray = new int[][]{{0,1,0}, {1,1,1}, {0,0,0}};
            case Z -> shapeArray = new int[][]{{1,1,0}, {0,1,1}, {0,0,0}};
        }
    }

    public Pos getPosition() {
        return this.position;
    }

    public void setPosition(Pos newPosition) {
        this.position = newPosition;
    }

    public boolean rotate(boolean clockwise) {
        int[][] newShapeArray = this.getTurnedShapeArray(clockwise);
        return checkCollisionAndMove(this.position, newShapeArray);
    }

    public boolean moveDown() {
        Pos newPosition = this.position.sub(0, 1, 0);
        return checkCollisionAndMove(newPosition, this.shapeArray);
    }

    public boolean moveLeft() {
        Pos newPosition = this.position.sub(1, 0, 0);
        return checkCollisionAndMove(newPosition, this.shapeArray);
    }

    public boolean moveRight() {
        Pos newPosition = this.position.add(1, 0, 0);
        return checkCollisionAndMove(newPosition, this.shapeArray);
    }

    public void draw() {
        getBlockPositions().forEach(pos -> this.instance.setBlock(pos, this.getColoredBlock()));
    }

    public void remove() {
        this.getBlockPositions().forEach(pos -> this.instance.setBlock(pos, Block.AIR));
    }


    private Block getColoredBlock() {
        Block returnBlock;
        switch (this.shape) {
            case I -> returnBlock = Block.LIGHT_BLUE_CONCRETE;
            case J -> returnBlock = Block.BLUE_CONCRETE;
            case L -> returnBlock = Block.ORANGE_CONCRETE;
            case O -> returnBlock = Block.YELLOW_CONCRETE;
            case S -> returnBlock = Block.GREEN_CONCRETE;
            case T -> returnBlock = Block.PURPLE_CONCRETE;
            case Z -> returnBlock = Block.RED_CONCRETE;
            default -> returnBlock = Block.WHITE_CONCRETE;
        }
        return returnBlock;
    }

    private int[][] getTurnedShapeArray(boolean clockwise) {
        int iterations = 1;
        if(!clockwise) iterations = 3;

        int arrayLength = this.shapeArray.length;
        int[][] startArray = Arrays.stream(this.shapeArray).map(int[]::clone).toArray(int[][]::new);
        int[][] returnArray = new int[arrayLength][arrayLength];

        for(int k=0; k<iterations; k++) {
            for(int i=0; i<arrayLength; i++) {
                for(int j=0; j<arrayLength; j++) {
                    returnArray[i][arrayLength-1-j] = startArray[j][i];
                }
            }
            startArray = Arrays.stream(returnArray).map(int[]::clone).toArray(int[][]::new);
        }

        return returnArray;
    }

    private boolean isPartOfTetromino(Pos position) {
        for(Pos blockPosition : getBlockPositions()) {
            if(position.sameBlock(blockPosition)) {
                return true;
            }
        }
        return false;
    }

    private List<Pos> getBlockPositions() {
        return this.getBlockPositions(this.position, this.shapeArray);
    }

    private List<Pos> getBlockPositions(Pos position, int[][] shapeArray) {
        List<Pos> returnList = new ArrayList<>();
        if(position == null) return returnList;

        int arrayLength = shapeArray.length;

        for(int x=0; x<arrayLength; x++) {
            for(int y=0; y<arrayLength; y++) {
                if(shapeArray[arrayLength-1-y][x] == 1) {
                    switch (this.shape) {
                        case I -> returnList.add(position.add(x-1, y-2, 0));
                        case O -> returnList.add(position.add(x, y, 0));
                        default -> returnList.add(position.add(x-1, y-1, 0));
                    }
                }
            }
        }

        return returnList;
    }

    private boolean checkCollision(Pos newPosition, int[][] newShapeArray) {
        List<Pos> newBlockPositions = getBlockPositions(newPosition, newShapeArray);

        for(Pos pos : newBlockPositions) {
            if(isPartOfTetromino(pos)) continue;
            if(this.instance.getBlock(pos) != Block.AIR) return true;
        }

        return false;
    }

    private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) {
        if(!checkCollision(newPosition, newShapeArray)) {
            this.remove();
            this.shapeArray = Arrays.stream(newShapeArray).map(int[]::clone).toArray(int[][]::new);
            this.setPosition(newPosition);
            this.draw();
            return true;
        }
        return false;
    }
}
