package de.ctdo.crashtest.game;

import de.ctdo.crashtest.log.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class Statemachine implements IStatemachine {
    public enum state {
        IDLE,
        ENTERED_ROOM,
        TABLE_GAME_ONE,
        TABLE_GAME_TWO,
        TABLE_GAME_THREE,
        TABLE_GAME_FOUR,
        TABLE_GAME_FIVE,
        TABLE_GAME_SIX,
        TABLE_GAME_DONE,
        ROKET_STARTED,
        ROKET_DONE,
        NEUTRAL
    }

    private final char RESET = '1';
    private final char BLUE_BUTTON = 'E';
    private final char LIGHT_BARRIER = 'F';
    private final char TABLE_ONE = 'G';
    private final char TABLE_TWO = 'H';
    private final char TABLE_THREE = 'I';
    private final char ROKET_INPUT = 'B';
    private final List<StatemachineListener> statemachineListenerList;

    private Timer timer;

    private long lastHandleInput;
    private int stateChangeCounter;
    private state currentState;
    private int timertSecondsLast;
    private int timertSeconds;


    public Statemachine() {
        currentState = state.IDLE;
        statemachineListenerList = new ArrayList<StatemachineListener>();
    }

    @Override
    public void addListener(StatemachineListener listener) {
        statemachineListenerList.add(listener);
    }

    @Override
    public state getCurrentState() {
        return currentState;
    }
    
    @Override
    public void setNewState(state newState) {
        currentState = newState;
        stateChangeCounter++;
        onStateChanged();
    }

    @Override
    public int getStateChangeCounter() {
        return stateChangeCounter;
    }

    @Override
    public void reset() {
        stateChangeCounter = 0;
        currentState = state.IDLE;
        stopTimer();
        onStateChanged();
    }

    @Override
    public void handleInput(char input) {
        if(System.currentTimeMillis() - lastHandleInput < 200 ) return;

        state newState = getNewState(input);

        if( newState != currentState ) {
            stateChangeCounter++;
            currentState = newState;
            onStateChanged();
        }

        lastHandleInput = System.currentTimeMillis();
    }

    @Override
    public int getTimerSecondsLast() {
        return timertSecondsLast / 10;
    }

    @Override
    public int getTimerSeconds() {
        return timertSeconds / 10;
    }

    @Override
    public void startTimer(int seconds) {
        timertSecondsLast = seconds*10;
        timertSeconds = seconds*10;
        scheduleTimer();
    }

    @Override
    public void stopTimer() {
        if(timer != null) timer.cancel();
        timertSecondsLast = 0;
        timertSeconds = 0;
    }

    @Override
    public void pauseTimer(boolean pause) {
        if(pause) {
            if(timer != null) timer.cancel();
        } else {
            scheduleTimer();
        }
    }


    /**
     * notifies all listeners about the changed state
     */
    private void onStateChanged() {
        for(StatemachineListener listener: statemachineListenerList) {
            listener.stateChanged(currentState);
        }
    }

    private void onTimerTick() {
        for(StatemachineListener listener: statemachineListenerList) {
            listener.timerTick(timertSecondsLast);
        }
    }

    /**
     * Calculates the new game state from the current state and new input button
     * @param input  the char from the input
     * @return  returns the new state
     */
    private state getNewState(char input) {
        state retVal = currentState;

        if(input == RESET) {
            retVal = state.IDLE;
        } else {

            switch (currentState) {
                case IDLE:
                    if(input == LIGHT_BARRIER) {
                        retVal = state.ENTERED_ROOM;
                    }
                    break;
                case ENTERED_ROOM:
                    if(input == TABLE_ONE) {
                        retVal = state.TABLE_GAME_ONE;
                    }
                    break;
                case TABLE_GAME_ONE:
                    if(input == TABLE_TWO) {
                        retVal = state.TABLE_GAME_TWO;
                    }
                    break;
                case TABLE_GAME_TWO:
                    if(input == TABLE_THREE) {
                        retVal = state.TABLE_GAME_THREE;
                    } else if (input == TABLE_ONE) {
                        retVal = state.TABLE_GAME_ONE;
                    }
                    break;
                case TABLE_GAME_THREE:
                    if(input == TABLE_TWO) {
                        retVal = state.TABLE_GAME_FOUR;
                    } else if (input == TABLE_ONE) {
                        retVal = state.TABLE_GAME_ONE;
                    }
                    break;
                case TABLE_GAME_FOUR:
                    if(input == TABLE_THREE) {
                        retVal = state.TABLE_GAME_FIVE;
                    } else if (input == TABLE_ONE) {
                        retVal = state.TABLE_GAME_ONE;
                    }
                    break;
                case TABLE_GAME_FIVE:
                    if(input == TABLE_ONE) {
                        retVal = state.TABLE_GAME_SIX;
                    } else if (input == TABLE_TWO) {
                        retVal = state.TABLE_GAME_ONE;
                    }
                    break;
                case TABLE_GAME_SIX:
                    if(input == TABLE_THREE) {
                        retVal = state.TABLE_GAME_DONE;
                    } else if (input == TABLE_TWO) {
                        retVal = state.TABLE_GAME_ONE;
                    }
                    break;
                case TABLE_GAME_DONE:
                    if(input == BLUE_BUTTON) {
                        retVal = state.ROKET_STARTED;
                    }
                    break;
                case ROKET_STARTED:
                    if(input == ROKET_INPUT) {
                        retVal = state.ROKET_DONE;
                    }
            }
        }

        return retVal;
    }


    private void scheduleTimer() {
        if(timer != null) timer.cancel();
        
        timer = new Timer();
        
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                timertSecondsLast--;

                onTimerTick();

                if(timertSecondsLast <= 0) {
                    if(timer != null) timer.cancel();
                }
            }
        };
        timer.scheduleAtFixedRate(timerTask, 100, 100);
    }



}
