package de.frajul.endlessroll.main.game;

import android.view.MotionEvent;
import android.view.View;

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

import de.frajul.endlessroll.data.Vector;
import de.frajul.endlessroll.entities.DestroyEffect;
import de.frajul.endlessroll.entities.Obstacle;
import de.frajul.endlessroll.entities.Player;
import de.frajul.endlessroll.entities.collectables.Energy;
import de.frajul.endlessroll.entities.collectables.Star;
import de.frajul.endlessroll.entities.collision.CollisionDetector;
import de.frajul.endlessroll.entities.collision.CollisionManager;
import de.frajul.endlessroll.entities.guis.Countdown;
import de.frajul.endlessroll.entities.guis.ToolButton;
import de.frajul.endlessroll.entities.guis.ToolButtonBar;
import de.frajul.endlessroll.entities.particles.Firework;
import de.frajul.endlessroll.entities.particles.ParticleSystem;
import de.frajul.endlessroll.entities.textures.TexturePack;
import de.frajul.endlessroll.entities.tools.Bomb;
import de.frajul.endlessroll.entities.tools.Tool;
import de.frajul.endlessroll.entities.tools.ToolType;
import de.frajul.endlessroll.levels.Level;
import de.frajul.endlessroll.levels.LevelPack;
import de.frajul.endlessroll.main.GameActivity;
import de.frajul.endlessroll.main.GameHandler;
import de.frajul.endlessroll.main.GameLog;
import de.frajul.endlessroll.main.physics.Physics;
import de.frajul.endlessroll.main.screens.GameScreen;
import de.frajul.endlessroll.main.screens.Screen;
import de.frajul.endlessroll.rendering.Rendering;
import de.frajul.endlessroll.sqlDatabase.MyDatabase;
import de.frajul.endlessroll.views.ViewManager;

/**
 * Created by Julian on 26.11.2015.
 */
public class Game extends Rendering<GameScene> {

    private GameActivity gameActivity;
    private GameHandler handler;
    private ViewManager viewManager;
    private LevelPack levelPack;
    private ParticleSystem particleSystem;
    private Firework firework;
    private Countdown countdown;
    private ToolButtonBar toolButtonBar;

    private ToolType currentTool;
    private Player player;
    private Physics physics;
    private CollisionManager collisionManager;
    private CollisionDetector collisionDetector;
    private Timer timer;
    private GameState gameState = GameState.COUNTDOWN;

    private Level level;
    private List<Integer> collectedStars = new ArrayList<>();
    private boolean energyCollected;

    public Game(GameHandler handler, GameScreen gameScreen, GameActivity gameActivity) throws Exception {
        super(gameActivity);
        this.handler = handler;
        this.gameActivity = gameActivity;
        physics = new Physics();
        collisionManager = new CollisionManager(this);
        collisionDetector = new CollisionDetector();
        particleSystem = new ParticleSystem(getContext());
        viewManager = new ViewManager(this, gameScreen, handler, gameActivity);
    }

    @Override
    public GameScene init(TexturePack texturePack, Timer timer, boolean isFirstTime) {
        GameLog.d("init Game");
        this.timer = timer;
        try {
            if (isFirstTime) {
                scene = new GameScene(gameActivity, texturePack, particleSystem);
                firework = new Firework(particleSystem.firework, scene.getCamera());
                countdown = new Countdown(this, gameActivity.getSoundManager(), texturePack);
                toolButtonBar = new ToolButtonBar(texturePack, this,
                        gameActivity.getUser().getToolSlotSettings());
                scene.getGuis().addAll(toolButtonBar.toGuiList());
                scene.getGuis().add(countdown);
                if (level != null)
                    startGame(levelPack, level);
            } else {
                scene.setTexturePack(texturePack);
            }
            particleSystem.loadTextures();
        } catch (Exception e) {
            onException(e);
        }
        return scene;
    }

    public void startGame(LevelPack levelPack, Level level) {
        GameLog.d("Start game");
        try {
            this.level = level;
            this.levelPack = levelPack;
            if (scene != null) {
                gameActivity.getSoundManager().getCurrentGameMusic().reset();
                gameActivity.getSoundManager().randomizeCurrentGameMusic();
                gameActivity.getSoundManager().stopAllSounds();
                gameState = GameState.COUNTDOWN;
                gameActivity.getTutorialManager().resetGameTutorials();
                collectedStars.clear();
                energyCollected = false;
                particleSystem.deleteAllSources();
                scene.loadLevel(level, levelPack.getWorld(),
                        gameActivity.getUser().getCurrentPlayerShape());
                player = scene.getPlayer();
                viewManager.resetViews();
                toolButtonBar.reset(gameActivity.getUser().getToolSlotSettings());
                setCurrentTool(toolButtonBar.getActiveButton().getToolType(), true);
                viewManager.setShowFps(gameActivity.getDataStorageHandler().readIsShowFps());
                countdown.start();
            }
        } catch (Exception e) {
            onException(e);
        }
    }

    public void countdownFinished() {
        gameActivity.getSoundManager().resumeAllSounds();
        gameActivity.getSoundManager().getCurrentGameMusic().start();
        gameState = GameState.RUNNING;
    }

    @Override
    public void setScreenSize(int width, int height) {
        Vector screenSize = new Vector(width, height);
        scene.setScreenSize(screenSize);
    }

    @Override
    public void update() {
        try {
            particleSystem.update(timer);
            if (scene == null || player == null)
                return;

            float playerProgress = player.getProgress();
            viewManager.update(gameState == GameState.RUNNING, timer, playerProgress);
            toolButtonBar.update(timer.getFrameTimeSeconds(), gameState == GameState.RUNNING);
            switch (gameState) {
                case RUNNING:
                    if (player.getPosition().y < -2f) {
                        onGameOver(false);
                        return;
                    }
                    if (player.getPosition().x >= scene.getGoalX()) {
                        onGoalReached();
                        return;
                    }
                    scene.getCamera().update(player.getPosition().y, timer);

                    gameActivity.getTutorialManager().update(level, playerProgress);

                    physics.applyGravity(scene, timer);
                    scene.update(timer);
                    collisionManager.update(physics, scene, timer);
                    break;
                case COUNTDOWN:
                    countdown.update(timer.getFrameTimeSeconds());
                    break;
            }
        } catch (Exception e) {
            onException(e);
        }
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        try {
            if (gameState == GameState.RUNNING && event.getAction() == MotionEvent.ACTION_DOWN) {
                Vector touchPoint = scene.calcWorldFromScreenCoords(event.getX(), event.getY());

                for (ToolButton toolButton : toolButtonBar.getToolButtons()) {
                    ToolType toolType = toolButton.getToolType();
                    if (collisionDetector.isPointQuadCollision(touchPoint,
                            toolButton.getBorder()) && toolType != null) {
                        toolButtonBar.setActive(toolType);
                        setCurrentTool(toolType, toolButton.hasFinishedLoading());
                        return true;
                    }
                }
                touchPoint.translate(scene.camera.getX(), scene.camera.getY());
                ToolButton currentToolButton = toolButtonBar.getActiveButton();
                if (currentToolButton != null && currentToolButton
                        .hasFinishedLoading() && currentToolButton.getToolType() != null) {
                    if (currentTool == ToolType.BOMB) {
                        Obstacle selectedObstacle = scene.getObstacleAtPoint(touchPoint);
                        if (selectedObstacle == null) {
                            viewManager.showBombErrorMessage(event.getX(), event.getY());
                            return true;
                        } else {
                            currentToolButton.setProgress(0);
                            scene.getBombSelected().set(false);
                            Tool tool = addTool(touchPoint);
                            if (tool != null && tool instanceof Bomb)
                                ((Bomb) tool).setAttachedObstacle(selectedObstacle);
                            return true;
                        }
                    }
                    currentToolButton.setProgress(0);
                    addTool(touchPoint);
                    return true;
                }
            }
            return false;
        } catch (Exception e) {
            onException(e);
            return false;
        }
    }

    public void resetViews() {
        viewManager.resetViews();
        toolButtonBar.reset(gameActivity.getUser().getToolSlotSettings());
    }

    public void continueGame() {
        viewManager.hideShortMenu();
        gameState = GameState.COUNTDOWN;
        viewManager.setShowFps(gameActivity.getDataStorageHandler().readIsShowFps());
        countdown.start();
    }

    public void startNextLevel() {
        level = levelPack.getNextLevel(level);
        startGame(levelPack, level);
    }

    public void restartLevel() {
        startGame(levelPack, level);
    }

    public void toLevelsScreen() {
        handler.toScreen(Screen.ScreenType.LEVELS);
    }

    public void onToolButtonFinishedLoading(ToolType toolType) {
        if (currentTool == ToolType.BOMB && toolType == ToolType.BOMB)
            scene.getBombSelected().set(true);
    }

    public void setCurrentTool(ToolType toolType, boolean toolButtonFinishedLoading) {
        currentTool = toolType;
        if (currentTool == ToolType.BOMB && toolButtonFinishedLoading) {
            scene.getBombSelected().set(true);
        } else {
            scene.getBombSelected().set(false);
        }
    }

    public void pauseWithoutMenu() {
        gameState = GameState.PAUSED;
    }

    public void tryToPause() {
        if (gameState == GameState.GAME_OVER || gameState == GameState.LEVEL_FINISHED || gameState == GameState.PAUSED)
            return;
        gameActivity.getSoundManager().pauseAllSounds();
        gameActivity.getSoundManager().getCurrentGameMusic().pause();
        viewManager.showShortMenu();
        if (gameState == GameState.COUNTDOWN)
            countdown.stop();
        gameState = GameState.PAUSED;
    }

    public void onGoalMessageKeyBack() {
        viewManager.onGoalMessageKeyBack();
    }

    public void setRunning() {
        gameState = GameState.RUNNING;
    }

    private Tool addTool(Vector position) {
        try {
            gameActivity.getSoundManager().playSound(gameActivity.getSoundManager().placeToolSound);
            return scene.addTool(currentTool, position, physics);
        } catch (Exception e) {
            onException(e);
        }
        return null;
    }

    public void onGameOver(boolean playerExplode) {
        gameActivity.getSoundManager().getCurrentGameMusic().stop();
        gameActivity.getSoundManager().stopAllSounds();
        gameActivity.getSoundManager().playSound(gameActivity.getSoundManager().dieSound);
        if (playerExplode) {
            scene.getUncategorizedEntities().remove(player);
            DestroyEffect.EXPLOSION.createEffect(particleSystem, player.getPosition(),
                    new Vector(player.getWidth(), player.getHeight())).start();
        }
        gameState = GameState.GAME_OVER;
        viewManager.showGameOverMessage();
    }

    private void onGoalReached() {
        gameActivity.getSoundManager().getCurrentGameMusic().stop();
        gameActivity.getSoundManager().stopAllSounds();
        gameActivity.getSoundManager().playSound(gameActivity.getSoundManager().goalSound);

        gameState = GameState.LEVEL_FINISHED;
        if (!level.isFinished())
            gameActivity.getUser().gainLvFinishedEp();
        level.setFinished(true);

        for (int i = 0; i <= 2; i++) {
            if (collectedStars.contains(i)) {
                level.setStarCollected(i, true);
                gameActivity.getUser().onStarCollected();
            }
        }

        if (energyCollected) {
            level.setEnergyCollected(true);
            gameActivity.getUser().onEnergyCollected();
        }

        firework.start();
        gameActivity.getSoundManager().playSound(gameActivity.getSoundManager().fireworkSound);

        viewManager.showGoalMessage(levelPack, level);

        MyDatabase database = gameActivity.getDataStorageHandler().getDatabase();
        database.open();
        database.writeLevelProgress(level);

        gameActivity.showNewFinishedPlayerShapeTasks();

        Level nextLevel = levelPack.getNextLevel(level);
        if (nextLevel != null) {
            nextLevel.setLocked(false);
            database.writeLevelProgress(nextLevel);
        } else {
            LevelPack nextLevelPack = gameActivity.getLevelManager().getNextLevelPack(levelPack);
            if (nextLevelPack != null) {
                nextLevelPack.setLocked(false);
                database.writeLevelPackLocked(nextLevelPack);
            }
        }
        database.close();
        gameActivity.getDataStorageHandler().writeUserData(gameActivity.getUser());
    }

    public void onStarCollision(Star star) {
        if (!star.isDestroyed()) {
            scene.onStarCollision(star);
            gameActivity.getSoundManager()
                    .playSound(gameActivity.getSoundManager().collectStarSound);
            collectedStars.add(star.getIndex());
        }
    }

    public void onEnergyCollision(Energy energy) {
        if (!energy.isDestroyed()) {
            scene.onEnergyCollision(energy);
            gameActivity.getSoundManager()
                    .playSound(gameActivity.getSoundManager().collectEnergySound);
            energyCollected = true;
        }
    }

    @Override
    public void onException(Exception e) {
        handler.onException(e);
    }

    public GameState getGameState() {
        return gameState;
    }

    public ViewManager getViewManager() {
        return viewManager;
    }
}
