package meerkat.pollingstation;

import com.google.common.util.concurrent.FutureCallback;
import meerkat.pollingstation.controller.PollingStationControllerInterface;
import meerkat.pollingstation.controller.commands.PollingStationCommand;
import meerkat.pollingstation.controller.commands.ReceivedScanCommand;
import meerkat.protobuf.PollingStation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.LinkedBlockingQueue;

/**
 * Created by Laura on 3/20/2017.
 */
public class PollingStationMainController implements PollingStationControllerInterface {
    private final Logger logger = LoggerFactory.getLogger(PollingStationMainController.class);

    private LinkedBlockingQueue<PollingStationCommand> queue;
    private volatile boolean shutDownHasBeenCalled;

    public PollingStationMainController() {
        queue = new LinkedBlockingQueue<PollingStationCommand>();
        shutDownHasBeenCalled = false;
    }

    @Override
    public void init(PollingStationScanner.PollingStationServer server) {
        server.subscribe(new FutureCallback<PollingStation.ScannedData>() {
            @Override
            public void onSuccess(PollingStation.ScannedData result) {
                if (result.getDataCase().equals(PollingStation.ScannedData.DataCase.BALLOT)) {
                    // TODO: Set request ID correctly?
                    int requestID = 1;
                    long serialNumber = result.getBallot().getSignedEncryptedBallot().getEncryptedBallot().getSerialNumber();
                    queue.add(new ReceivedScanCommand(requestID, serialNumber));
                } else if  (result.getDataCase().equals(PollingStation.ScannedData.DataCase.ERROR)) {
                    // TODO: Deal with scan errors
                }
            }

            @Override
            public void onFailure(Throwable t) {
                // TODO: deal with this? When is this called?
            }
        });
    }

    public void run() {
        logger.info("Started controller");
            while (! wasShutDownCalled()) {
                try {
                    PollingStationCommand command = queue.take();
                    handleSingleCommand(command);
                }
                catch (InterruptedException e) {
                    System.err.println("Interrupted while reading from command queue " + e);
                }
            }
    }

    private boolean wasShutDownCalled () {
        return shutDownHasBeenCalled;
    }

    /**
     * this method decides upon a given command if to ignore it (if it has an old serial number) or to handle it
     * If we choose to handle it, then it simply calls the matching method which handles this type of command
     * @param command a command to handle next (probably from the inner command queue)
     */
    private void handleSingleCommand(PollingStationCommand command) {
        // check if the command is old and should be ignored
//        if (command.getBallotSerialNumber() != state.currentBallotSerialNumber && !(command instanceof RestartVotingCommand)) {
//            // probably an old command relating to some old ballot serial number. Simply log it and ignore it.
//            String errorMessage = "handleSingleCommand: received a task too old. " +
//                    command.getBallotSerialNumber() + " " + state.currentBallotSerialNumber;
//            logger.debug(errorMessage);
//            return;
//        }

        // decide which method to run according to the command type
        if (command instanceof ReceivedScanCommand) {
            doProcessScan((ReceivedScanCommand) command);
        }
//        else if (command instanceof ChannelChoiceCommand) {
//            doChooseChannel();
//        }
        else {
            logger.error("handleSingleCommand: unknown type of PollingStationCommand received: " + command.getClass().getName());
//            doReportErrorAndForceRestart(systemMessages.get(StorageManager.SOMETHING_WRONG_MESSAGE));
            doReportErrorAndForceRestart("error message to define");
        }
    }

    private void doProcessScan(ReceivedScanCommand command) {
        logger.info("The Polling Station starts processing the data...");
        logger.info("Ballot Serial Number = "+command.getBallotSerialNumber());
    }

    /**
     * a (overloaded) method to report an error message to the voter
     * @param errorMessage message to show the voter
     */
    private void doReportErrorAndForceRestart(String errorMessage) {
        queue.clear();
    }

    @Override
    // TODO: 4/16/2017 Implement the shutdown behavior
    public void callShutDown() {

    }
}
