/*
 * 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.math.Vector3f;
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.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import org.wyrez.shootingstars.data.AudioDataManager;
import org.wyrez.shootingstars.controls.PlayerCamControl;
import org.wyrez.shootingstars.controls.PlayerMouseControl;
import org.wyrez.shootingstars.controls.PlayerMoveControl;
import org.wyrez.shootingstars.controls.PlayerShootControl;
import org.wyrez.shootingstars.factories.Materials;
import org.wyrez.shootingstars.game.Cinema;
import org.wyrez.shootingstars.game.GameSettings;
import org.wyrez.shootingstars.game.Ground;
import org.wyrez.shootingstars.gui.GameGUI;
import org.wyrez.shootingstars.gui.listener.GameListener;
import org.wyrez.shootingstars.helper.UserDataKeys;
import tonegod.gui.core.Screen;
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 {

    private StateManager stateManager;
    private AssetManager assetManager;
    private InputManager inputManager;
    private ViewPort viewPort;
    private Camera camera;
    private GameSettings settings;
    private AudioDataManager audioDataManager;
    private final MediaPlayerFactory mediaPlayerFactory;
    private DirectMediaPlayer mediaPlayer;
    private Node rootNode;
    private GameGUI gui;
    private Cinema cinema;
    private Ground ground;
    private Spatial player;
    private boolean isRunning = false;

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

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

        this.inputManager = inputManager;
        this.camera = camera;
        this.audioDataManager = audioDataManager;
        this.assetManager = assetManager;
        this.viewPort = viewPort;

        mediaPlayerFactory = new MediaPlayerFactory("--no-video-title-show", "--quiet");

        this.settings = settings;
    }

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

    public void initMediaPlayer() throws Exception {
        mediaPlayer = mediaPlayerFactory.newDirectMediaPlayer(settings.getVideoFormat(),
                settings.getVideoWidth(), settings.getVideoHeight(),
                settings.getVideoWidth() * settings.getVideoDepth(), cinema);
        mediaPlayer.prepareMedia(settings.getVideoFile());
    }

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

    public void loadPlayer() {
        player = new Geometry("player", new Box(Vector3f.ZERO, 1f, 1f, 1f)); //TODO start location?
        player.setMaterial(Materials.UNSHADED.create());
        player.setUserData(UserDataKeys.RUNNING, false);
        player.addControl(new PlayerMouseControl(inputManager, camera));
        player.addControl(new PlayerMoveControl(audioDataManager));
        player.addControl(new PlayerShootControl(inputManager, gui));
        player.addControl(new PlayerCamControl(camera));
    }

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

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

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

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

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

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

    @Override
    public void cleanup() {
        super.cleanup();
        setRunning(false);
        mediaPlayer.release();
        mediaPlayerFactory.release();
    }

    public DirectMediaPlayer getMediaPlayer() {
        return mediaPlayer;
    }

    public boolean isRunning() {
        return isRunning;
    }

    public void onAction(String name, boolean isPressed, float tpf) {
        if (name.equals("ESC") && !isPressed) {
            setRunning(false);
            mediaPlayer.pause();
            inputManager.setCursorVisible(true);
            gui.showMenu();
        }
    }

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

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