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.GameObject;
import de.thdeg.greiner.superpangworld.graphics.immovable.*;
import de.thdeg.greiner.superpangworld.graphics.moveable.*;

import java.awt.Color;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

/**
 * The manager handling the display and passive movement of the game objects.
 */
class GameObjectManager {

    private final LinkedList<Bubble> bubbles;
    private final LinkedList<Harpoon> harpoons;

    private final LevelProgressBar levelProgressBar;
    private final LevelLabel levelLabel;
    private final ScoreLabel scoreLabel;
    private final LivesLabel livesLabel;
    private final Background background;
    private final GameFrame frame;
    private final Overlay overlay;

    private final PlayerObject playerObject;

    private final LinkedList<GameObject> gameObjects;

    private final Random random;

    /**
     * Create a manager handling the game objects.
     *
     * @param gameView the GameView
     */
    public GameObjectManager(GameView gameView){

        random = new Random();
        gameView.setColorForBlockImage('k', Color.LIGHT_GRAY);

        bubbles = new LinkedList<>();
        harpoons = new LinkedList<>();
        gameObjects = new LinkedList<>();

        levelProgressBar = new LevelProgressBar(gameView);
        levelLabel = new LevelLabel(gameView);
        scoreLabel = new ScoreLabel(gameView);
        livesLabel = new LivesLabel(gameView);
        background = new Background(gameView);
        frame = new GameFrame(gameView);
        playerObject = new PlayerObject(gameView);
        overlay = new Overlay(gameView);
    }

    /**
     * Clear all game object lists and stops any sounds.
     */
    public void clearAll(){
        gameObjects.clear();
        bubbles.clear();
        harpoons.forEach(Harpoon::stopSound);
        harpoons.clear();
    }

    /**
     * Get all collideable game objects a bubble can collide with.
     *
     * @return the game objects
     */
    public List<CollidableGameObject> getBubblesCollideables(){
        List<CollidableGameObject> collidableGameObjects = new ArrayList<>();
        collidableGameObjects.add(playerObject);
        collidableGameObjects.addAll(harpoons);
        return collidableGameObjects;
    }

    /**
     * Add bubbles to the game.
     *
     * @param newBubbles the list of new bubbles
     * @param gamePlayManager the associated game play manager
     */
    public void addBubbles(List<Bubble> newBubbles,GamePlayManager gamePlayManager){
        List<CollidableGameObject> collidableGameObjects = getBubblesCollideables();
        newBubbles.forEach(bubble->{
            bubble.setGamePlayManager(gamePlayManager);
            bubble.addCollidableGameObject(collidableGameObjects);
            bubbles.add(bubble);
        });
    }

    /**
     * Add a random bubble to the game.
     *
     * @param specialBubblePresent flag, if a special bubble is already present in the game
     * @param gameView the game view for the bubble
     * @param gamePlayManager the game play manager for the bubble
     * @return <code>true</code> if a special bubble was added, otherwise <code>false</code>
     */
    public boolean addRandomBubble(boolean specialBubblePresent, GameView gameView, GamePlayManager gamePlayManager){
        boolean specialBubbleAdded = false;
        int randomNumber = specialBubblePresent ? random.nextInt(90) : random.nextInt(100);
        List<CollidableGameObject> collidableGameObjects = getBubblesCollideables();
        Bubble newBubble;
        if(randomNumber<60){
            newBubble = new RoundBubble(gameView, collidableGameObjects);
        }else if(randomNumber<90){
            newBubble = new HexagonalBubble(gameView, collidableGameObjects);
        }else{
            newBubble = new SpecialBubble(gameView, collidableGameObjects);
            specialBubbleAdded = true;
        }
        newBubble.setGamePlayManager(gamePlayManager);
        bubbles.add(newBubble);
        return specialBubbleAdded;
    }

    /**
     * Add the objects to the canvas and handle update their positions.
     */
    public void updateGameObjects(){
        gameObjects.clear();
        gameObjects.add(background);
        gameObjects.addAll(harpoons);
        gameObjects.add(frame);
        gameObjects.add(levelProgressBar);
        gameObjects.add(levelLabel);
        gameObjects.add(scoreLabel);
        gameObjects.add(livesLabel);
        gameObjects.add(playerObject);
        gameObjects.addAll(bubbles);
        gameObjects.add(overlay);

        gameObjects.forEach(gameObject ->{
            gameObject.update();
            gameObject.addToCanvas();
        });
    }

    /**
     * Get the player object.
     *
     * @return the player
     */
    public PlayerObject getPlayerObject(){
        return playerObject;
    }

    /**
     * Get the bubbles.
     *
     * @return the bubbles
     */
    public LinkedList<Bubble> getBubbles() {
        return bubbles;
    }

    /**
     * Get the level label.
     *
     * @return the level label
     */
    public LevelLabel getLevelLabel(){
        return levelLabel;
    }

    /**
     * Get the lives label.
     *
     * @return the lives label
     */
    public LivesLabel getLivesLabel() {
        return livesLabel;
    }

    /**
     * Get the score label.
     *
     * @return the score label
     */
    public ScoreLabel getScoreLabel() {
        return scoreLabel;
    }

    /**
     * Get the level progress bar.
     *
     * @return the level progress bar
     */
    public LevelProgressBar getLevelProgressBar(){
        return levelProgressBar;
    }

    /**
     * Get the harpoons.
     *
     * @return the harpoons
     */
    public LinkedList<Harpoon> getHarpoons() {
        return harpoons;
    }

    /**
     * Get the game overlay.
     *
     * @return the game overlay
     */
    public Overlay getGameOverlay(){
        return overlay;
    }

    /**
     * Get the background object.
     *
     * @return the background object
     */
    public Background getBackground() {
        return background;
    }
}