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

import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.util.BatchUtil;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import net.minestom.server.instance.block.Block;
import org.apache.commons.lang3.ArrayUtils;

import java.util.Random;

public class Playfield {
    private final Pos lowerLeftCorner;
    private final StatelessGame instance;
    private final static int height = 22;
    private final static Block scoreBlock = Block.STONE;
    private final int nextTetrominoesCount;
    private final Random random;

    public Playfield(Pos lowerLeftCorner, StatelessGame instance, int nextTetrominoesCount) {
        this.nextTetrominoesCount = nextTetrominoesCount;
        this.lowerLeftCorner = lowerLeftCorner;
        this.instance = instance;
        this.random = new Random();
    }

    public Pos getPlayerSpawnPosition() {
        return this.lowerLeftCorner.add(6, 9, 20).withView(180, 0);
    }

    public Pos getTetrominoSpawnPosition() {
        return this.lowerLeftCorner.add(5, 21, 1);
    }

    public Pos getHoldPosition() {
        return this.lowerLeftCorner.add(-4, 18, 1);
    }

    public Pos getNextPosition() {
        return this.lowerLeftCorner.add(15, 18, 1);
    }

    public Pos getScorePosition() {
        return this.lowerLeftCorner.add(-5, height+3, 0);
    }

    public void generate() {
        AbsoluteBlockBatch batch = new AbsoluteBlockBatch();

        // actual playfield:
        for(int x=0; x<12; x++) {
            for(int y = 0; y< height; y++) {
                batch.setBlock(this.lowerLeftCorner.add(x, y, 0), Block.GLASS);
                batch.setBlock(this.lowerLeftCorner.add(x, y, -1), Block.BLACK_CONCRETE);

                if(x==0 || x==11 || y==0) {
                    batch.setBlock(this.lowerLeftCorner.add(x, y, 1), Block.GRAY_CONCRETE);
                    batch.setBlock(this.lowerLeftCorner.add(x, y, 0), Block.GRAY_CONCRETE);
                }
            }
        }

        // hold position:
        for(int x = 0; x < 4; x++) {
            for(int y = 0; y < 4; y++) {
                batch.setBlock(this.getHoldPosition().add(x-1, y-1, -1), Block.QUARTZ_BLOCK);
            }
        }

        // next positions:
        for(int x = 0; x < 4; x++) {
            for(int y = -4*this.nextTetrominoesCount+4; y < 4; y++) {
                batch.setBlock(this.getNextPosition().add(x-1, y-1, -1), Block.QUARTZ_BLOCK);
            }
        }

        batch.setBlock(getPlayerSpawnPosition().sub(0, 1, 0), Block.STONE);
        batch.setBlock(getPlayerSpawnPosition().sub(1, 1, 0), Block.STONE);
        batch.setBlock(getPlayerSpawnPosition().sub(1, 1, 1), Block.STONE);
        batch.setBlock(getPlayerSpawnPosition().sub(0, 1, 1), Block.STONE);

        BatchUtil.loadAndApplyBatch(batch, this.instance, () -> {});
    }

    public int removeFullLines() {
        int removedLinesCounter = 0;
        for(int y = 1; y< height; y++) {
            boolean isFullLine = true;
            for(int x=1; x<11; x++) {
                if(this.instance.getBlock(this.lowerLeftCorner.add(x, y, 1)) == Block.AIR) isFullLine = false;
            }
            if(isFullLine) {
                removeFullLine(y);
                removedLinesCounter += 1;
                y -= 1;
            }
        }
        return removedLinesCounter;
    }

    public void addLines(int lines) {
        int xPosMissing = random.nextInt(1, 10);

        for (int i = 0; i < lines; i++) {
            moveAllLinesUp();
            for (int x = 1; x < 11; x++) {
                if(x != xPosMissing) {
                    this.instance.setBlock(this.lowerLeftCorner.add(x, 1, 1), Block.LIGHT_GRAY_CONCRETE);
                } else {
                    this.instance.setBlock(this.lowerLeftCorner.add(x, 1, 1), Block.AIR);
                }
            }
        }
    }

    public void updateAttackingLines(int attackingLines) {
        for (int y = 0; y < height + 5; y++) {
            if(attackingLines > 0) {
                this.instance.setBlock(this.lowerLeftCorner.add(12, y, 1), Block.REDSTONE_BLOCK);
                attackingLines -= 1;
            } else {
                this.instance.setBlock(this.lowerLeftCorner.add(12, y, 1), Block.AIR);
            }
        }
    }

    public void updateScore(int score) {
        this.removeDigits();
        String scoreString = String.valueOf(score);
        char[] characters = scoreString.toCharArray();
        ArrayUtils.reverse(characters);
        for(int i = 6; i > 0; i--) {
            char digit;
            if(i <= characters.length) {
                digit = characters[i-1];
            } else {
                digit = '0';
            }
            this.displayDigit(digit, 6-i);
        }
    }


    private void displayDigit(char digit, int positionFromLeft) {
        int[][] digitArray;
        switch (digit) {
            case '1' -> digitArray = new int[][]{{0,0,1},{0,1,1},{0,0,1},{0,0,1},{0,0,1}};
            case '2' -> digitArray = new int[][]{{1,1,1},{0,0,1},{1,1,1},{1,0,0},{1,1,1}};
            case '3' -> digitArray = new int[][]{{1,1,1},{0,0,1},{0,1,1},{0,0,1},{1,1,1}};
            case '4' -> digitArray = new int[][]{{1,0,1},{1,0,1},{1,1,1},{0,0,1},{0,0,1}};
            case '5' -> digitArray = new int[][]{{1,1,1},{1,0,0},{1,1,1},{0,0,1},{1,1,1}};
            case '6' -> digitArray = new int[][]{{1,1,1},{1,0,0},{1,1,1},{1,0,1},{1,1,1}};
            case '7' -> digitArray = new int[][]{{1,1,1},{0,0,1},{0,1,0},{0,1,0},{0,1,0}};
            case '8' -> digitArray = new int[][]{{1,1,1},{1,0,1},{1,1,1},{1,0,1},{1,1,1}};
            case '9' -> digitArray = new int[][]{{1,1,1},{1,0,1},{1,1,1},{0,0,1},{1,1,1}};
            default -> digitArray = new int[][]{{1,1,1},{1,0,1},{1,0,1},{1,0,1},{1,1,1}};
        }

        for (int x = 0; x < 3; x++) {
            for (int y = 0; y < 5; y++) {
                if(digitArray[4-y][x] == 1) this.instance.setBlock(this.getScorePosition().add(positionFromLeft*4+x, y, 0), scoreBlock);
            }
        }
    }

    private void removeDigits() {
        for (int x = 0; x < 4 * 6; x++) {
            for (int y = 0; y < 5; y++) {
                this.instance.setBlock(this.getScorePosition().add(x, y, 0), Block.AIR);
            }
        }
    }

    private void moveAllLinesUp() {
        for (int y = height + 3; y > 1; y--) {
            for (int x = 1; x < 11; x++) {
                Block blockBeneath = this.instance.getBlock(this.lowerLeftCorner.add(x, y - 1, 1));
                this.instance.setBlock(this.lowerLeftCorner.add(x, y, 1), blockBeneath);
            }
        }
    }

    private void removeFullLine(int positionY) {
        for(int y = positionY; y< height; y++) {
            for(int x=1; x<11; x++) {
                Block blockAbove = this.instance.getBlock(this.lowerLeftCorner.add(x, y+1, 1));
                this.instance.setBlock(this.lowerLeftCorner.add(x, y, 1), blockAbove);
            }
        }
    }
}
