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

import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import net.kyori.adventure.text.Component;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.scoreboard.Sidebar;
import net.minestom.server.timer.Scheduler;
import net.minestom.server.timer.TaskSchedule;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class TetrisGame {
    private final StatelessGame instance;
    private final Playfield playfield;
    private int level = 1;
    private int lines = 0;
    private int score = 0;
    private boolean lost = false;
    public boolean paused = true;
    public Tetromino currentTetromino;
    private Tetromino nextTetromino;
    private Tetromino holdTetromino;
    private final List<Tetromino> tetrominoBag = new ArrayList<>();
    private boolean holdPossible = true;
    private final Pos holdPosition;
    private final Pos tetrominoSpawnPosition;
    public Sidebar sidebar = new Sidebar(Component.text("Info:"));

    public TetrisGame(StatelessGame instance, Pos lowerLeftCorner) {
        this(instance, lowerLeftCorner, new Tetromino(instance, Tetromino.Shape.J), new Tetromino(instance, Tetromino.Shape.T));
    }

    public TetrisGame(StatelessGame instance, Pos lowerLeftCorner, Tetromino startTetromino, Tetromino nextTetromino) {
        this.instance = instance;
        this.playfield = new Playfield(lowerLeftCorner, this.instance);
        this.currentTetromino = startTetromino;
        this.nextTetromino = nextTetromino;

        this.holdPosition = this.playfield.getHoldPosition();
        this.tetrominoSpawnPosition = this.playfield.getTetrominoSpawnPosition();
        this.buildSidebar();
    }

    public void start() {
        this.paused = false;
        Scheduler scheduler = MinecraftServer.getSchedulerManager();
        scheduler.submitTask(() -> {
            if(this.lost) return TaskSchedule.stop();
            if(this.paused) return TaskSchedule.tick(40/this.level);
            this.tick();
            return TaskSchedule.tick(40/this.level);
        });
    }

    public void generate() {
        this.playfield.generate();

        this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
        this.currentTetromino.draw();
    }

    public void tick() {
        if(this.lost || this.paused) return;
        if(!currentTetromino.moveDown()) {
            setActiveTetrominoDown();
        }
    }

    public void addPoints(int points) {
        this.score += points;
        this.updateSidebar();
    }


    public boolean rotate(boolean clockwise) {
        if(this.lost || this.paused) return false;
        return this.currentTetromino.rotate(clockwise);
    }

    public boolean moveLeft() {
        if(this.lost || this.paused) return false;
        return this.currentTetromino.moveLeft();
    }

    public boolean moveRight() {
        if(this.lost || this.paused) return false;
        return this.currentTetromino.moveRight();
    }

    public boolean moveDown() {
        if(this.lost || this.paused) return false;
        if(!this.currentTetromino.moveDown()) {
            this.setActiveTetrominoDown();
            return false;
        }
        this.score += 1;
        this.updateSidebar();
        return true;
    }

    public boolean switchHold() {
        if(!holdPossible) return false;
        if(this.lost || this.paused) return false;

        Tetromino newCurrentTetromino;
        if(this.holdTetromino == null) {
            newCurrentTetromino = this.nextTetromino;
            this.nextTetromino = this.getNextTetromino();
        } else {
            newCurrentTetromino = this.holdTetromino;
            this.holdTetromino.remove();
        }

        this.currentTetromino.remove();
        this.holdTetromino = this.currentTetromino;
        this.currentTetromino = newCurrentTetromino;

        this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
        this.currentTetromino.draw();
        if(!this.currentTetromino.moveDown()) loose();

        this.holdTetromino.setPosition(this.holdPosition);
        this.holdTetromino.draw();
        this.holdPossible = false;
        return true;
    }


    private Tetromino getNextTetromino() {
        if(this.tetrominoBag.isEmpty()) {
            for(Tetromino.Shape shape : Tetromino.Shape.values()) {
                this.tetrominoBag.add(new Tetromino(this.instance, shape));
            }
            Collections.shuffle(this.tetrominoBag);
        }

        return this.tetrominoBag.removeFirst();
    }

    private void loose() {
        this.lost = true;
    }

    private void buildSidebar() {
        this.sidebar.createLine(new Sidebar.ScoreboardLine(
            "0",
            Component.text("Score: "),
            0
        ));
        this.sidebar.createLine(new Sidebar.ScoreboardLine(
            "1",
            Component.text("Lines: "),
            0
        ));
        this.sidebar.createLine(new Sidebar.ScoreboardLine(
            "2",
            Component.text("Level: "),
            1
        ));
    }

    private void updateSidebar() {
        this.sidebar.updateLineScore("0",  this.score);
        this.sidebar.updateLineScore("1", this.lines);
        this.sidebar.updateLineScore("2", this.level);
    }

    private void setActiveTetrominoDown() {
        currentTetromino = nextTetromino;
        nextTetromino = getNextTetromino();

        int removedLines = this.playfield.removeFullLines();
        switch (removedLines) {
            case 1 -> {
                this.lines += 1;
                this.score += 40 * this.level;
            }
            case 2 -> {
                this.lines += 2;
                this.score += 100 * this.level;
            }
            case 3 -> {
                this.lines += 3;
                this.score += 300 * this.level;
            }
            case 4 -> {
                this.lines += 4;
                this.score += 1200 * this.level;
            }
        }

        this.level = (int) Math.floor((double) this.lines / 10) + 1;
        this.holdPossible = true;

        this.updateSidebar();

        currentTetromino.setPosition(this.tetrominoSpawnPosition);
        currentTetromino.draw();
        if(!currentTetromino.moveDown()) {
            loose();
        }
    }
}
