package meerkat.voting.gui.uiFX;

import meerkat.voting.gui.managment.VotingBoothGUIManager;
import meerkat.protobuf.Voting;
import meerkat.voting.controller.callbacks.*;
import meerkat.voting.gui.ui.CommandPend;
import meerkat.voting.gui.ui.TickerTimerTask;
import meerkat.voting.gui.ui.uicommands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.*;


/**
 * Created by Vladimir Eliezer Tokarev on 9/17/2016.
 * This class forwards and receives different information for voter voting process
 * and displays it in to voting booth gui
 */
public class GraphicalUserInterface implements Runnable {
    private static final Logger logger = LoggerFactory.getLogger(GraphicalUserInterface.class);

    private CommandPend<UICommand> cmdPend;
    private Date startWaitingTime;
    private VotingBoothGUIManager votingGUIManager;

    private volatile boolean shutDownHasBeenCalled;

    public GraphicalUserInterface() {
        final int tickDurationInMilliSeconds = 10; // period between view update calls

        logger.info("A Voting Booth graphical user interface is constructed");
        cmdPend = new CommandPend<>();

        votingGUIManager = new VotingBoothGUIManager();

        startWaitingTime = null;
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TickerTimerTask(cmdPend), new Date(), tickDurationInMilliSeconds);

        shutDownHasBeenCalled = false;
    }

    /**
     * the run() method. Simply loops and takes the UI's pending command and handles it accordingly
     */
    @Override
    public void run() {
        logger.info("Graphical user interface starts running");
        while (! shutDownHasBeenCalled) {
            try {
                UICommand command = cmdPend.take();
                handleSingleCommand(command);
            }
            catch (InterruptedException e) {
                logger.warn ("Interrupted while reading the pending command " + e);
            }
        }
    }

    /**
     * chooses the next method to run according to the type of the given UICommand.
     * Special case for the TickCommand.
     * As this command is registered in the queue constantly, we simply ignore this command if the UI is not in
     * a waiting state
     * @param command any valid UICommand
     */
    private void handleSingleCommand(UICommand command) {
        if (!(command instanceof TickCommand)) {
            if (startWaitingTime != null) {
                stopWaiting();
            }
        }

        if (command instanceof StartSessionUICommand) {
            doShowWelcomeScreen((StartSessionUICommand)command);
        }
        else if (command instanceof ChannelChoiceUICommand) {
            doAskChannelChoiceQuestions((ChannelChoiceUICommand)command);
        }
        else if (command instanceof RaceVotingUICommand) {
            doAskVotingQuestions((RaceVotingUICommand)command);
        }
        else if (command instanceof CastOrAuditUICommand) {
            doCastOrAudit ((CastOrAuditUICommand)command);
        }
        else if (command instanceof FatalErrorUICommand) {
            doFatalError((FatalErrorUICommand)command);
        }
        else if (command instanceof WaitForFinishUICommand) {
            doWaitForFinish((WaitForFinishUICommand)command);
        }
        else if (command instanceof TickCommand) {
            doTick ();
        }
        else {
            String errorMessage = "handleSingleCommand: unknown type of UICommand received: " +
                    command.getClass().getName();
            logger.error(errorMessage);
            throw new RuntimeException(errorMessage);
        }
    }

    /**
     * doTick do a simple action
     */
    private void doTick() {
    }

    /**
     * welcomes the new voter at the beginning of the session
     * @param command a StartSessionUICommand with a acallback
     */
    private void doShowWelcomeScreen(StartSessionUICommand command) {
        logger.debug("Graphical user interface entered doShowWelcomeScreen");
    }

    /**
     * marks that the waiting, for something else to have happened, is finished
     */
    private void stopWaiting () {
    }

    /**
     * lists the channel choice questions to the voter and gathers the voter's answers
     * @param command a ChannelChoiceUICommand with the data and a callback
     */
    private void doAskChannelChoiceQuestions (ChannelChoiceUICommand command) {
        logger.debug("Graphical user interface doAskChannelChoiceQuestions");
    }


    /**
     * lists the race voting questions to the voter and gathers the voter's answers
     * @param command a RaceVotingUICommand with a callback
     */
    private void doAskVotingQuestions (RaceVotingUICommand command) {
        logger.debug("Graphical user interface doAskVotingQuestions");
    }

    /**
     * asks the voter whether to cast or audit the ballot
     * @param command a simple CastOrAuditUICommand with the callback
     */
    private void doCastOrAudit(CastOrAuditUICommand command) {
        logger.debug("Graphical user interface entered doCastOrAudit");
    }


    /**
     * Tells the voter (in the console) to wait until some other process is finished
     * @param command a simple WaitForFinishUICommand with the callback
     */
    public void doWaitForFinish (WaitForFinishUICommand command) {
        logger.debug("Graphical user interface entered doWaitForFinish");
    }


    /**
     * show an error to the voter. let him press a (chosen) button for handling the error.
     * @param command a FatalErrorUICommand with the callback
     */
    private void doFatalError (FatalErrorUICommand command) {
        logger.debug("Graphical user interface entered doFatalError");
    }

    /**
     * present the questions to the voter console sequentially.
     * Voter may choose at any time to skip a question, go back or even cancel the whole session
     * @param questions list of questions to present
     * @return list of answers to the questions (at the same order)
     * @throws VoterCancelThrowable this is thrown if a voter chose to cancel in the middle of the process
     * @throws IOException
     */
    private List<Voting.BallotAnswer> askVoterForAnswers(List<Voting.BallotQuestion> questions) throws VoterCancelThrowable, IOException {
        return new ArrayList<Voting.BallotAnswer>();
    }
}
