package at.oefai.aaa;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.Date;
import java.util.concurrent.CountDownLatch;

import at.oefai.aaa.animation.AnimationEngine;
import at.oefai.aaa.animation.GraphicsBridge;
import at.oefai.aaa.stage.ActingStage;
import at.oefai.aaa.stage.StageException;
import at.oefai.aaa.stage.StageFactory;

/**
 * The State machine governing the major action (start,pause,load,save,reset,exit,synchronize).
 * @author Stefan Rank
 */
public class StateManager implements IState {
    private static enum State { EMPTY, LOADED, RUNNING, PAUSED }

    private State currentState = State.EMPTY; // state memory

    // adapters for listening to relevant events
    private final WindowAdapter windowAdapterBridge = new WindowAdapterBridge();
    // the animationengine used for displaying the world
    private final AnimationEngine animEngine = new AnimationEngine();
    // create a gui mediator for output stuff...
    private final IGUIMediator guiMediator = new AAAViewerFrame(this, this.windowAdapterBridge,
            this.animEngine.getDisplayingComponent());

    private ActingStage stage = null; // this holds the action

    private File lastUsedFile = null; // save last saved or loaded for reset

    public StateManager() {
        this.guiMediator.scheduleFirstShow();
        this.animEngine.registerListener(new GraphicsBridge(this.guiMediator, this, this.animEngine));
    }

    /**
     * Pause the action and reflect the state change.
     * @return true if now paused
     */
    public final boolean pause() {
        switch (this.currentState) {
            case RUNNING :
                this.guiMediator.deactivateGUI();
                CountDownLatch cdl = new CountDownLatch(1);
                this.stage.pause(cdl);
                try { // wait and block until everything is really paused
                    cdl.await();
                } catch (InterruptedException e) {
                    // ignore
                }
                this.currentState = State.PAUSED;
                this.guiMediator.setPaused();
                this.guiMediator.showStatus("Acting paused");
                this.guiMediator.activateDetailGUI();
                this.guiMediator.activateGUI();
            case PAUSED :
                return true;
            default :
                return false;
        }
    }

    /**
     * Start the agents.
     * @return true if now running
     */
    public final boolean start() {
        switch (this.currentState) {
            case PAUSED :
            case LOADED :
                this.guiMediator.deactivateGUI();
                CountDownLatch cdl = new CountDownLatch(1);
                this.stage.start(cdl);
                cdl.countDown(); // give the startSignal
                this.currentState = State.RUNNING;
                this.guiMediator.setRunning();
                this.guiMediator.showStatus("currently Acting...");
                this.guiMediator.deactivateDetailGUI();
                this.guiMediator.activateGUI();
            case RUNNING :
                return true;
            default :
                return false;
        }
    }

    /**
     * Load a new stage file.
     * @param pFile the stage description
     * @return true if successful
     */
    public final boolean load(final File pFile) {
        switch (this.currentState) {
            case PAUSED :
            case LOADED :
            case EMPTY :
                File f = pFile;
                if (f == null) {
                    f = this.guiMediator.getFileToLoad();
                    if (f == null) {
                        return false;
                    }
                }
                this.guiMediator.deactivateGUI();
                this.guiMediator.showStatus("...trying to load ActingStage from " + f.getPath());
                boolean result = loadFile(f);
                if (result) {
                    this.currentState = State.LOADED;
                    this.guiMediator.setPaused();
                    this.guiMediator.showStatus("ActingStage loaded", f);
                    // no activatGUI() here, triggered when animEngine/graphicsBridge is ready (svgloaddispatchcompleted)
                } else {
                    this.guiMediator.showStatus("Loading failed or canceled");
                    this.guiMediator.activateGUI();
                }
                return result;
            default :
                return false;
        }
    }

    private boolean loadFile(final File f) {
        assert f != null;
        this.stage = null; // discard last one
        System.gc(); //run garbage collector here
        StageFactory stageFactory = null;
        try {
            stageFactory = new StageFactory(f, this.animEngine, this.guiMediator.getDetailSink());
        } catch (StageException ex) {
            this.guiMediator.showException(ex);
            return false;
        }

        this.stage = stageFactory.getActingStage();

        this.lastUsedFile = f; // remember file for reset
        return true;
    }

    public final boolean save(final File pFile) {
        switch (this.currentState) {
            case PAUSED :
                File f = pFile;
                if (f == null) {
                    f = this.guiMediator.getFileToSave();
                    if (f == null) {
                        return false;
                    }
                }
                this.guiMediator.deactivateGUI();

                /** @todo save current ActingStage as snapshot */
                boolean result = false; // for now

                if (result) {
                    this.guiMediator.setPaused();
                    this.guiMediator.showStatus("Acting paused (last save at " + new Date().toString() + ")");
                    // remember to indicate new file in title via showStatus when implementing this
                }
                this.guiMediator.activateGUI();
                return result;
            default :
                return false;
        }
    }

    /**
     * Reload the last stage loaded from a file.
     * @return true if successful
     */
    public final boolean reset() {
        switch (this.currentState) {
            case PAUSED :
            case LOADED :
                return load(this.lastUsedFile);
            default :
                return false;
        }
    }

    /**
     * Final call in this programm.
     */
    public final void exit() {
        /** @todo ask for save and cleanup */
        this.guiMediator.deactivateGUI();
        this.guiMediator.showStatus("trying to exit...");
        this.pause(); // so that no unfinished threads stay around
        // save Prefs till next run
        AppPrefs.setFrameHeight(this.guiMediator.getHeight());
        AppPrefs.setFrameWidth(this.guiMediator.getWidth());
        AppPrefs.save();
        this.guiMediator.cleanUp();
        // for now just exit
        System.exit(0);
        this.guiMediator.activateGUI(); // unreachable ?
    }

    public final void synchronize() {
        this.stage.synchronizeGraphics();
    }

    /**
     * Implements the hooks for interesting Window Events.
     * @author wrstl
     */
    private class WindowAdapterBridge extends WindowAdapter {
        public void windowOpened(final WindowEvent e) {
            StateManager.this.guiMediator.justOpened();
        }

        public void windowClosing(final WindowEvent e) {
            exit();
            // if we reached this point close is automatically overriden
        }

        public void windowIconified(final WindowEvent e) {
            // automatically pause on iconify
            pause();
        }
    }

}
