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.CollidableGameObject;
import de.thdeg.greiner.superpangworld.graphics.base.Position;
import de.thdeg.greiner.superpangworld.graphics.helper.Level;
import de.thdeg.greiner.superpangworld.graphics.helper.Player;
import de.thdeg.greiner.superpangworld.graphics.moveable.Harpoon;
import de.thdeg.greiner.superpangworld.graphics.moveable.HexagonalBubble;
import de.thdeg.greiner.superpangworld.graphics.moveable.RoundBubble;
import de.thdeg.greiner.superpangworld.graphics.moveable.SpecialBubble;

import java.awt.*;
import java.util.*;
import java.util.List;

/**
 * The manager which handles the gameplay.
 */
public class GamePlayManager {

    /** The GameView on which the game is displayed */
    private final GameView gameView;
    /** The manager, which handles the game objects */
    private final GameObjectManager gameObjectManager;
    /** Generator for random values */
    private final Random random;
    /** The current level */
    private Level level;
    /** The player */
    private Player player;
    /** The level manager */
    private LevelManager levelManager;
    /** Flag, if bubbles should be spawned */
    private boolean spawnBubbles;
    /** Flag, if all bubbles should be destroyed */
    private boolean destroyAllBubbles;
    /** Flag, is a special bubble is currently present */
    public boolean specialBubblePresent;
    /** Flag, if the bubble movement is froze */
    public boolean movementFroze;

    /**
     * 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) {
        random = new Random();
        this.gameView = gameView;
        this.gameObjectManager = gameObjectManager;
        boolean easyDifficulty = gameView.showSimpleStartScreen("Super Pang World","Start the game");

        initializeGame(easyDifficulty);
        initializeLevel();
    }

    /**
     * Initialize the game.
     *
     * @param easyDifficulty the game difficulty
     */
    private void initializeGame(boolean easyDifficulty){
        gameObjectManager.clearAll();
        levelManager = new LevelManager(easyDifficulty);
        try {
            level = levelManager.getNextLevel();
        } catch (LevelManager.NoMoreLevelsAvailableException e) {
            System.out.println("No levels were generated, exiting program");
            System.exit(-1);
        }
        player = new Player(level);
        gameObjectManager.getPlayerObject().setGamePlayManager(this);
    }

    /**
     * Initialize the level.
     */
    private void initializeLevel(){
        // Set Gameplay variables
        spawnBubbles = true;
        destroyAllBubbles = false;
        specialBubblePresent = false;
        movementFroze = false;
        // Update HUD
        updateHUD();
    }

    /**
     * Loose a life and remove all bubbles
     */
    public void looseLife(){
        player.lives--;
        gameObjectManager.getLivesLabel().setLifeCount(player.lives);
        gameObjectManager.getBubbles().clear();
        specialBubblePresent = false;
        spawnBubbles = true;
        gameObjectManager.getGameOverlay().flashScreen(Color.red,100);
    }

    /**
     * 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) {
            gameView.showEndScreen("You won!");
            boolean easyDifficulty = gameView.showSimpleStartScreen("Super Pang World","Start the game");
            initializeGame(easyDifficulty);
            initializeLevel();
        }
    }

    /**
     * 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(player.score>=level.neededOverallScore){
            nextLevel();
        }

        if(gameView.timerExpired("SpawnBubble","GamePlayManager" ) && spawnBubbles && !movementFroze){
            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());
            for(Bubble bubble: bubblesCopy){
                destroy(bubble);
            }
            gameView.setTimer("DestroyAllBubbles","GamePlayManager",500);
        }
    }

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

    /**
     * Freeze the bubble movement.
     *
     * @param freezeTime the freeze duration
     */
    public void freezeMovement(int freezeTime){
        movementFroze = true;
        gameView.setTimer("MovementFroze","GamePlayManager",freezeTime);
    }

    /**
     * Remove a harpoon from the game.
     *
     * @param harpoon the harpoon to remove
     */
    public void destroy(Harpoon harpoon){
        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);
        }
    }

    /**
     * Add a random bubble with given values to the game. Percentages:
     *   - 60% normal bubble
     *   - 30% hexagonal bubble
     *   - 10% special bubble
     */
    private void addRandomBubble(){
        int randomNumber = specialBubblePresent ? random.nextInt(90) : random.nextInt(100);
        ArrayList<CollidableGameObject> collidableGameObjects = new ArrayList<>();
        collidableGameObjects.add(gameObjectManager.getPlayerObject());
        collidableGameObjects.addAll(gameObjectManager.getHarpoons());

        if(randomNumber<60){
            RoundBubble roundBubble = new RoundBubble(gameView, collidableGameObjects);
            roundBubble.setGamePlayManager(this);
            gameObjectManager.getBubbles().add(roundBubble);
        }else if(randomNumber<90){
            HexagonalBubble hexagonalBubble = new HexagonalBubble(gameView, collidableGameObjects);
            hexagonalBubble.setGamePlayManager(this);
            gameObjectManager.getBubbles().add(hexagonalBubble);
        }else{
            SpecialBubble specialBubble = new SpecialBubble(gameView, collidableGameObjects);
            specialBubble.setGamePlayManager(this);
            gameObjectManager.getBubbles().add(specialBubble);
            specialBubblePresent = true;
        }
    }

    /**
     * Add bubbles to the game.
     *
     * @param bubbles the bubbles
     */
    public void addBubbles(Bubble... bubbles){
        ArrayList<CollidableGameObject> collidableGameObjects = new ArrayList<>();
        collidableGameObjects.add(gameObjectManager.getPlayerObject());
        collidableGameObjects.addAll(gameObjectManager.getHarpoons());
        for(Bubble bubble: bubbles){
            bubble.setGamePlayManager(this);
            bubble.addCollidableGameObject(collidableGameObjects);
            gameObjectManager.getBubbles().add(bubble);
        }
    }

    /**
     * Shoot a harpoon starting at a given position.
     *
     * @param startPosition the start position
     * @return if a harpoon was shot or not
     */
    public boolean shootHarpoon(Position startPosition){
        if(gameView.timerExpired("ShootHarpoon","GamePlayManager") && gameObjectManager.getHarpoons().size()<2){
            Harpoon harpoon = new Harpoon(gameView,new ArrayList<>());
            harpoon.getPosition().setTo(startPosition.x+16, startPosition.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;
    }
}
