/*
 * Copyright (C) 2013 Darth Affe <http://wyrez.org> and contributors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.wyrez.shootingstars.states;

import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.input.InputManager;
import com.jme3.input.controls.ActionListener;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.BloomFilter;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import org.wyrez.shootingstars.data.AudioDataManager;
import org.wyrez.shootingstars.factories.Particles;
import org.wyrez.shootingstars.game.Cinema;
import org.wyrez.shootingstars.game.GameSettings;
import org.wyrez.shootingstars.game.Ground;
import org.wyrez.shootingstars.game.MediaPlayerEventListenerBase;
import org.wyrez.shootingstars.game.Player;
import org.wyrez.shootingstars.game.StarManager;
import org.wyrez.shootingstars.gui.GameGUI;
import org.wyrez.shootingstars.gui.listener.GameListener;
import org.wyrez.shootingstars.gui.manager.HighscoreManager;
import org.wyrez.shootingstars.helper.ScreenHelper;
import org.wyrez.shootingstars.helper.UserDataKeys;
import org.wyrez.shootingstars.states.util.OptionSettings;
import tonegod.gui.core.Screen;
import uk.co.caprica.vlcj.player.MediaPlayer;
import uk.co.caprica.vlcj.player.MediaPlayerFactory;
import uk.co.caprica.vlcj.player.direct.DirectMediaPlayer;

/**
 *
 * @author Darth Affe
 */
public class GameState extends AbstractAppState implements GameListener, ActionListener {

    public static final float FIELD_RADIUS = 1000f;
    public static final float FIELD_HEIGHT = 550f;
    public static final float FIELD_LOWER_POS = 200f;
    /**/
    private StateManager stateManager;
    private AssetManager assetManager;
    private InputManager inputManager;
    private ViewPort viewPort;
    private Camera camera;
    private GameSettings settings;
    private AudioDataManager audioDataManager;
    private StarManager starManager;
    private MediaPlayerFactory mediaPlayerFactory;
    private DirectMediaPlayer mediaPlayer;
    private Node rootNode;
    private GameGUI gui;
    private Cinema cinema;
    private Ground ground;
    private Node player;
    private Node starNode;
    private OptionSettings optionSettings;
    private HighscoreManager highscoreManager;
    private boolean isRunning = false;
    private boolean isFinished = false;

    public GameState(Node rootNode, Screen screen, StateManager stateManager,
            AssetManager assetManager, ViewPort viewPort, OptionSettings optionSettings,
            InputManager inputManager, Camera camera, AudioDataManager audioDataManager,
            GameSettings settings, ScreenHelper screenHelper, HighscoreManager highscoreManager) {

        this.rootNode = rootNode;
        this.gui = new GameGUI(screen, this, assetManager, screenHelper);
        this.stateManager = stateManager;
        inputManager.addListener(this, new String[]{"ESC"});

        this.inputManager = inputManager;
        this.camera = camera;
        this.audioDataManager = audioDataManager;
        this.assetManager = assetManager;
        this.viewPort = viewPort;
        this.optionSettings = optionSettings;
        this.highscoreManager = highscoreManager;
        this.settings = settings;

        Particles.setParticleDensity(optionSettings.getParticleDensity());
    }

    public void loadGround() {
        ground = new Ground(audioDataManager, settings, FIELD_RADIUS);
        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
        fpp.addFilter(new BloomFilter(BloomFilter.GlowMode.Objects));
        viewPort.addProcessor(fpp);
    }

    public void initMediaPlayer() throws Exception {
        if (settings.useVideo() || !optionSettings.isAudioVisualizationEnabled()) {
            mediaPlayerFactory = new MediaPlayerFactory("--no-video-title-show", "--quiet");
        } else {
            mediaPlayerFactory = new MediaPlayerFactory("--no-video-title-show", "--quiet",
                    "--audio-visual=visual", "--effect-list=scope");
        }
        mediaPlayer = mediaPlayerFactory.newDirectMediaPlayer(settings.getVideoFormat(),
                settings.getVideoWidth(), settings.getVideoHeight(),
                settings.getVideoWidth() * settings.getVideoDepth(), cinema);

        mediaPlayer.addMediaPlayerEventListener(new MediaPlayerEventListenerBase() {
            @Override
            public void finished(MediaPlayer mp) {
                isFinished = true;
            }
        });
        mediaPlayer.prepareMedia(settings.getVideoFile());
    }

    public void loadCinema() {
        cinema = new Cinema(settings, FIELD_RADIUS);
        cinema.move(0f, 160f, 0f);
    }

    public void loadPlayer() {
        starNode = new Node();
        player = new Player(rootNode, stateManager, assetManager, viewPort, gui,
                inputManager, camera, audioDataManager, starNode, optionSettings);

        gui.setPlayer(player);

        //Out of possition, but it needs an initialized player...
        this.starManager = new StarManager(audioDataManager, player, starNode);
    }

    private void setRunning(boolean running) {
        isRunning = running;
        player.setUserData(UserDataKeys.RUNNING, running);
    }

    public void start() {
        gui.setStart();
        inputManager.setCursorVisible(false);
        mediaPlayer.setVolume(optionSettings.getMusicVolume()
                * Math.round((float) optionSettings.getMasterVolume() / 100f));
        mediaPlayer.play();
        setRunning(true);
    }

    @Override
    public void stateAttached(AppStateManager stateManager) {
        rootNode.attachChild(cinema);
        rootNode.attachChild(ground);
        rootNode.attachChild(player);
        rootNode.attachChild(starNode);
        gui.setWait();
        gui.attach();
    }

    @Override
    public void stateDetached(AppStateManager stateManager) {
        gui.detach();
        rootNode.detachChild(starNode);
        rootNode.detachChild(player);
        rootNode.detachChild(ground);
        rootNode.detachChild(cinema);
        inputManager.setCursorVisible(true);
    }

    @Override
    public void render(RenderManager rm) {
        cinema.render();
    }

    @Override
    public void update(float tpf) {
        if (isRunning) {
            audioDataManager.update(tpf);
            starManager.update(tpf);
        }
        if (isFinished) {
            finished();
        }
    }

    @Override
    public void cleanup() {
        super.cleanup();
        setRunning(false);
        mediaPlayer.release();
        mediaPlayerFactory.release();
        mediaPlayer = null;
        mediaPlayerFactory = null;
        player = null;
        ground = null;
        cinema = null;
        starManager = null;
        audioDataManager.cleanup();
    }

    public DirectMediaPlayer getMediaPlayer() {
        return mediaPlayer;
    }

    public boolean isRunning() {
        return isRunning;
    }

    public void onAction(String name, boolean isPressed, float tpf) {
        if (name.equals("ESC") && !isPressed) {
            if (isRunning) {
                pause();
            } else {
                resume();
            }
        }
    }

    public void pause() {
        setRunning(false);
        player.setUserData(UserDataKeys.SHOOTING, false);
        mediaPlayer.pause();
        inputManager.setCursorVisible(true);
        gui.showMenu();
    }

    public void resume() {
        gui.resumeGame();
        inputManager.setCursorVisible(false);
        mediaPlayer.play();
        setRunning(true);
    }

    public void finished() {
        isFinished = false;
        highscoreManager.addScore(settings, optionSettings, Integer.valueOf(player.getUserData(UserDataKeys.POINTS).toString()));        
        stateManager.setState(State.HIGHSCORE);
    }

    public void cancelTrack() {
        stateManager.setState(State.MENU);
    }
}
