package de.thdeg.greiner.superpangworld.game.managers;

import de.thdeg.greiner.superpangworld.gameview.GameView;
import de.thdeg.greiner.superpangworld.graphics.base.Bubble;
import de.thdeg.greiner.superpangworld.graphics.base.Position;
import de.thdeg.greiner.superpangworld.graphics.helper.*;
import de.thdeg.greiner.superpangworld.graphics.moveable.Harpoon;

import java.awt.Color;
import java.util.*;

/**
 * The manager which handles the gameplay.
 */
public class GamePlayManager {
    /** Flag, if a special bubble is present */
    public boolean specialBubblePresent;
    /** Flag, if the bubble movement is frozen */
    public boolean movementFroze;
    private final GameView gameView;
    private final GameObjectManager gameObjectManager;
    private Level level;
    private Player player;
    private LevelManager levelManager;
    private boolean spawnBubbles;
    private boolean destroyAllBubbles;

    /**
     * Create the manager handling the gameplay.
     *
     * @param gameView the GameView for visualization
     * @param gameObjectManager the manager handling the game objects
     */
    public GamePlayManager(GameView gameView, GameObjectManager gameObjectManager) {
        this.gameView = gameView;
        this.gameObjectManager = gameObjectManager;
        initializeGame();
    }

    /**
     * Initialize the game.
     */
    private void initializeGame(){
        StartScreen startScreen = new StartScreen(gameView);
        startScreen.showStartScreen();
        boolean difficultySetToEasy = startScreen.isDifficultySetToEasy();
        gameView.playSound("backgroundMusic.wav", true);
        levelManager = new LevelManager(difficultySetToEasy);
        try {
            level = levelManager.getNextLevel();
        } catch (LevelManager.NoMoreLevelsAvailableException e) {
            System.exit(-1);
        }
        player = new Player(level);
        gameObjectManager.getPlayerObject().setGamePlayManager(this);
        gameObjectManager.getPlayerObject().setPosition(GameView.WIDTH/2.0, 360);
        initializeLevel();
    }

    /**
     * Start the next game or quit the game entirely.
     */
    private void nextGame(){
        gameView.stopAllSounds();
        gameObjectManager.clearAll();
        EndScreen endScreen = new EndScreen(gameView);
        String message = player.lives<0 ? "You lost the game.\n Your score: "+player.score : "You won the game.\n Your score: "+player.score;
        endScreen.showEndScreen(message);
        initializeGame();
    }

    /**
     * Initialize the level.
     */
    private void initializeLevel(){
        spawnBubbles = true;
        destroyAllBubbles = false;
        specialBubblePresent = false;
        movementFroze = false;
        updateHUD();
        gameObjectManager.getBackground().setBackgroundImage(level.backgroundImage);
    }

    /**
     * Switch to the next level or end the game if every level was played.
     */
    private void nextLevel(){
        try {
            level = levelManager.getNextLevel();
            initializeLevel();
        } catch (LevelManager.NoMoreLevelsAvailableException e) {
            nextGame();
        }
    }

    /**
     * Loose a life and remove all bubbles.
     */
    public void looseLife(){
        player.lives--;
        if(player.lives<0){
            nextGame();
        }else {
            gameObjectManager.clearAll();
            specialBubblePresent = false;
            spawnBubbles = true;
            gameObjectManager.getGameOverlay().flashScreen(Color.red, 100);
        }
    }

    /**
     * Update all HUD elements.
     */
    private void updateHUD(){
        gameObjectManager.getLevelLabel().setLevel(level.number);
        gameObjectManager.getLivesLabel().setLifeCount(player.lives);
        gameObjectManager.getScoreLabel().setScore(player.score);
        double progress = (player.score- level.neededOverallScore+ level.neededLevelScore * 1.0) / level.neededLevelScore;
        gameObjectManager.getLevelProgressBar().setLevelProgress(Math.min((int)(progress*100),100));
    }

    /**
     * Handle the gameplay.
     */
    public void updateGamePlay(){
        updateHUD();
        if(!destroyAllBubbles && player.score>=level.neededOverallScore){
            nextLevel();
        }
        if(spawnBubbles && !movementFroze && gameView.timerExpired("SpawnBubble","GamePlayManager" )){
            addRandomBubble();
            gameView.setTimer("SpawnBubble","GamePlayManager",3000);
        }
        if(movementFroze && gameView.timerExpired("MovementFroze","GamePlayManager")){
            movementFroze = false;
        }
        if(destroyAllBubbles && gameView.timerExpired("DestroyAllBubbles","GamePlayManager")){
            List<Bubble> bubblesCopy = new ArrayList<>(gameObjectManager.getBubbles());
            if(bubblesCopy.size()>0) {
                bubblesCopy.forEach(this::destroy);
                gameView.setTimer("DestroyAllBubbles", "GamePlayManager", 300);
            }else{
                destroyAllBubbles = false;
            }
        }
    }

    /**
     * Start the timer to destroy all bubbles.
     */
    public void destroyAllBubbles(){
        gameView.setTimer("DestroyAllBubbles","GamePlayManager",300);
        spawnBubbles = false;
        destroyAllBubbles = true;
        movementFroze = false;
    }

    /**
     * Freeze the bubble movement.
     *
     * @param freezeTime the freeze duration
     */
    public void freezeMovement(int freezeTime){
        movementFroze = true;
        gameView.playSound("freeze.wav",false);
        gameView.setTimer("MovementFroze","GamePlayManager",freezeTime);
        gameObjectManager.getGameOverlay().showMessage("Freeze "+ (freezeTime/1000)+" sec",freezeTime);
    }

    /**
     * Remove a harpoon from the game.
     *
     * @param harpoon the harpoon to remove
     */
    public void destroy(Harpoon harpoon){
        harpoon.stopSound();
        gameObjectManager.getHarpoons().remove(harpoon);
    }

    /**
     * Remove a bubble from the game.
     *
     * @param bubble the bubble to remove
     */
    public void destroy(Bubble bubble){
        bubble.destroy();
        gameObjectManager.getBubbles().remove(bubble);
        player.score += bubble.getBurstScore();
    }

    /**
     * Destroy a bubble colliding with a harpoon.
     *
     * @param harpoon the harpoon
     * @param bubble the bubble
     */
    public void destroy(Harpoon harpoon, Bubble bubble){
        if(gameObjectManager.getHarpoons().contains(harpoon) && gameObjectManager.getBubbles().contains(bubble)){
            destroy(harpoon);
            destroy(bubble);
        }
    }

    /**
     * Spawn a random bubble into the game.
     */
    private void addRandomBubble(){
        boolean specialBubbleAdded = gameObjectManager.addRandomBubble(specialBubblePresent, gameView, this);
        specialBubblePresent = specialBubblePresent || specialBubbleAdded;
    }

    /**
     * Add bubbles to the game.
     *
     * @param bubbles the bubbles
     */
    public void addBubbles(List<Bubble> bubbles){
        gameObjectManager.addBubbles(bubbles, this);
    }

    /**
     * Shoot a harpoon starting at a given position.
     *
     * @param playerPosition the player position
     * @return if a harpoon was shot or not
     */
    public boolean shootHarpoon(Position playerPosition){
        if(gameView.timerExpired("ShootHarpoon","GamePlayManager") && gameObjectManager.getHarpoons().size()<2){
            Harpoon harpoon = new Harpoon(gameView,new ArrayList<>(), new Position(playerPosition.x+6, playerPosition.y-15));
            harpoon.setGamePlayManager(this);
            gameObjectManager.getHarpoons().add(harpoon);
            gameObjectManager.getBubbles().forEach(b -> b.addCollidableGameObject(Collections.singletonList(harpoon)));
            gameView.setTimer("ShootHarpoon","GamePlayManager",300);
            return true;
        }
        return false;
    }
}