package meerkat.voting.gui;

import com.google.protobuf.ByteString;
import meerkat.crypto.DigitalSignature;
import meerkat.crypto.Encryption;
import meerkat.protobuf.Voting;
import meerkat.voting.ToyEncryption;
import meerkat.voting.ToySignature;
import meerkat.voting.controller.VotingBoothImpl;
import meerkat.voting.encryptor.VBCryptoManager;
import meerkat.voting.encryptor.VBCryptoManagerImpl;
import meerkat.voting.gui.uiFX.GraphicalUI;
import meerkat.voting.gui.output.FXWindowOutputDevice;
import meerkat.voting.output.NetworkVirtualPrinter;
import meerkat.voting.storage.StorageManager;
import meerkat.voting.storage.StorageManagerMockup;

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

/**
 * Created by Laura on 12/12/2016.
 */
public class VotingBoothToyGraphicalRun {

//    public static void main(String[] args) {
//        Application.launch(MainFX.class, args);
//    }


    public static void main(String[] args) {

        try {
            generateSystemMessages();
            generateDemoQuestions();
        }
        catch (Exception e) {
            return;
        }

        Random rand = new Random();
        Encryption enc = new ToyEncryption();
        DigitalSignature sig = new ToySignature("MY_SIGNER_ID");

        StorageManager storageManager = new StorageManagerMockup();
        FXWindowOutputDevice outputDevice = new FXWindowOutputDevice();
//        NetworkVirtualPrinter outputDevice = new NetworkVirtualPrinter("http://localhost:8080");
        VBCryptoManager cryptoManager = new VBCryptoManagerImpl(rand, enc, sig);
//        SystemConsoleUI ui = new SystemConsoleUI ();
        GraphicalUI ui = new GraphicalUI();

        VotingBoothImpl controller = new VotingBoothImpl();

        try {
            controller.init(outputDevice, cryptoManager, ui, storageManager);
        }
        catch (Exception e) {
            System.err.println("init failed");
            return;
        }


        // create threads


        Thread controllerThread = new Thread(controller);
        controllerThread.setName("Meerkat VB-Controller Thread");
        Thread uiThread = new Thread(ui);
        uiThread.setName("Meerkat VB-UI Thread");
        Thread outputThread = new Thread(outputDevice);
        outputThread.setName("Meerkat VB-Output Thread");

        uiThread.start();
        controllerThread.start();
        outputThread.start();

    }


    private static void generateDemoQuestions() throws IOException {

        Voting.ElectionParams electionParams = Voting.ElectionParams.newBuilder()
                .addAllRaceQuestions(generateBallotQuestions())
                .addAllChannelChoiceQuestions(generateChannelChoiceQuestions())
                .setSelectionData(generateSelectionData())
                .build();

        try {
            FileOutputStream output = new FileOutputStream(StorageManagerMockup.electionParamFullFilename);
            electionParams.writeTo(output);
            output.close();
            System.out.println("Successfully wrote election parameter protobuf to a file");
        }
        catch (IOException e) {
            System.err.println("Could not write to the election parameter file: '" + StorageManagerMockup.electionParamFullFilename + "'.");
            throw e;
        }

    }


    private static List<Voting.BallotQuestion> generateChannelChoiceQuestions() {
        ArrayList<Voting.BallotQuestion> channelChoiceQuestions = new ArrayList();

        String[] ans1 = {"Red", "Blue", "Green"};
        Voting.BallotQuestion ccquestion1 = generateBallotQuestion("What is your favorite color?", "Pick one answer", ans1);
        channelChoiceQuestions.add(ccquestion1);

        String[] ans2 = {"Yes", "No"};
        Voting.BallotQuestion ccquestion2 = generateBallotQuestion("Are you a republican?", "Pick one answer", ans2);
        channelChoiceQuestions.add(ccquestion2);

        return channelChoiceQuestions;
    }


    private static List<Voting.BallotQuestion> generateBallotQuestions() {
        ArrayList<Voting.BallotQuestion> allBallotQuestions = new ArrayList();

        String[] answers1 = {"answer 1", "answer 2", "answer 3", "answer 4"};
        allBallotQuestions.add(generateBallotQuestion("question 1. Asking something...", "Pick one answer", answers1));

        String[] answers2 = {"Miranda Kerr", "Doutzen Kroes", "Moran Atias", "Roslana Rodina", "Adriana Lima"};
        allBallotQuestions.add(generateBallotQuestion("question 2: Which model do you like", "Mark as many as you want", answers2));

        allBallotQuestions.add(generateBallotQuestion("question 3. Asking something...", "Pick one answer", answers1));
        allBallotQuestions.add(generateBallotQuestion("question 4. Asking something...", "Pick one answer", answers1));

        String[] answers5 = {"Clint Eastwood", "Ninja", "Sonic", "Tai-chi", "Diablo", "Keanu"};
        allBallotQuestions.add(generateBallotQuestion("question 5: Good name for a cat", "Pick the best one", answers5));

        allBallotQuestions.add(generateBallotQuestion("question 6. Asking something...", "Pick one answer", answers1));
        allBallotQuestions.add(generateBallotQuestion("question 7. Asking something...", "Pick one answer", answers1));
        allBallotQuestions.add(generateBallotQuestion("question 8. Asking something...", "Pick one answer", answers1));
        allBallotQuestions.add(generateBallotQuestion("question 9. Asking something...", "Pick one answer", answers1));
        allBallotQuestions.add(generateBallotQuestion("question 10. Asking something...", "Pick one answer", answers1));
        allBallotQuestions.add(generateBallotQuestion("question 11. Asking something...", "Pick one answer", answers1));
        allBallotQuestions.add(generateBallotQuestion("question 12. Asking something...", "Pick one answer", answers1));
        allBallotQuestions.add(generateBallotQuestion("question 13. Asking something...", "Pick one answer", answers1));
        allBallotQuestions.add(generateBallotQuestion("question 14. Asking something...", "Pick one answer", answers1));

        return allBallotQuestions;
    }


    private static Voting.BallotQuestion generateBallotQuestion(String questionStr, String descriptionStr, String[] answers) {
        Voting.UIElement question = Voting.UIElement.newBuilder()
                .setType(Voting.UIElementDataType.TEXT)
                .setData(stringToBytes(questionStr))
                .build();

        Voting.UIElement description = Voting.UIElement.newBuilder()
                .setType(Voting.UIElementDataType.TEXT)
                .setData(stringToBytes(descriptionStr))
                .build();

        Voting.BallotQuestion.Builder bqb = Voting.BallotQuestion.newBuilder();
        bqb.setIsMandatory(false);
        bqb.setQuestion(question);
        bqb.setDescription(description);
        for (String answerStr : answers) {
            Voting.UIElement answer = Voting.UIElement.newBuilder()
                    .setType(Voting.UIElementDataType.TEXT)
                    .setData(stringToBytes(answerStr))
                    .build();
            bqb.addAnswer(answer);
        }

        return bqb.build();
    }


    private static Voting.SimpleCategoriesSelectionData generateSelectionData() {
        Voting.Category sharedDefaults = Voting.Category.newBuilder()
                .addQuestionIndex(0)
                .addQuestionIndex(5)
                .addQuestionIndex(9)
                .build();

        Voting.Category cat00 = Voting.Category.newBuilder()
                .addQuestionIndex(1)
                .addQuestionIndex(4)
                .addQuestionIndex(6)
                .addQuestionIndex(7)
                .build();

        Voting.Category cat01 = Voting.Category.newBuilder()
                .addQuestionIndex(2)
                .addQuestionIndex(4)
                .addQuestionIndex(8)
                .build();

        Voting.Category cat02 = Voting.Category.newBuilder()
                .addQuestionIndex(3)
                .addQuestionIndex(8)
                .build();

        Voting.Category cat10 = Voting.Category.newBuilder()
                .addQuestionIndex(10)
                .addQuestionIndex(11)
                .build();

        Voting.Category cat11 = Voting.Category.newBuilder()
                .addQuestionIndex(12)
                .addQuestionIndex(13)
                .build();

        Voting.CategoryChooser catChooser0 = Voting.CategoryChooser.newBuilder()
                .addCategory(cat00)
                .addCategory(cat01)
                .addCategory(cat02)
                .build();

        Voting.CategoryChooser catChooser1 = Voting.CategoryChooser.newBuilder()
                .addCategory(cat10)
                .addCategory(cat11)
                .build();

        return Voting.SimpleCategoriesSelectionData.newBuilder()
                .setSharedDefaults(sharedDefaults)
                .addCategoryChooser(catChooser0)
                .addCategoryChooser(catChooser1)
                .build();

    }



    private static ByteString stringToBytes (String s) {
        return ByteString.copyFromUtf8(s);
    }


    private static void generateSystemMessages() throws IOException{
        Map<String, Voting.UIElement> systemMessageMap = new HashMap();

        systemMessageMap.put(StorageManager.WAIT_FOR_COMMIT_MESSAGE, Voting.UIElement.newBuilder()
                .setType(Voting.UIElementDataType.TEXT)
                .setData(ByteString.copyFromUtf8("Please wait while committing to ballot"))
                .build());
        systemMessageMap.put(StorageManager.WAIT_FOR_AUDIT_MESSAGE, Voting.UIElement.newBuilder()
                .setType(Voting.UIElementDataType.TEXT)
                .setData(ByteString.copyFromUtf8("Please wait while auditing your ballot"))
                .build());
        systemMessageMap.put(StorageManager.WAIT_FOR_CAST_MESSAGE, Voting.UIElement.newBuilder()
                .setType(Voting.UIElementDataType.TEXT)
                .setData(ByteString.copyFromUtf8("Please wait while finalizing your ballot for voting"))
                .build());
        systemMessageMap.put(StorageManager.RESTART_VOTING_BUTTON, Voting.UIElement.newBuilder()
                .setType(Voting.UIElementDataType.TEXT)
                .setData(ByteString.copyFromUtf8("Restart voting"))
                .build());
        systemMessageMap.put(StorageManager.UNRECOGNIZED_FINALIZE_RESPONSE_MESSAGE, Voting.UIElement.newBuilder()
                .setType(Voting.UIElementDataType.TEXT)
                .setData(ByteString.copyFromUtf8("Could not understand response for Cast or Audit. Force restarting."))
                .build());
        systemMessageMap.put(StorageManager.UNSUCCESSFUL_CHANNEL_CHOICE_MESSAGE, Voting.UIElement.newBuilder()
                .setType(Voting.UIElementDataType.TEXT)
                .setData(ByteString.copyFromUtf8("Choice of channel was unsuccessful. Force restarting."))
                .build());
        systemMessageMap.put(StorageManager.OUTPUT_DEVICE_FAILURE_MESSAGE, Voting.UIElement.newBuilder()
                .setType(Voting.UIElementDataType.TEXT)
                .setData(ByteString.copyFromUtf8("Ballot output device failure. Force restarting."))
                .build());
        systemMessageMap.put(StorageManager.UNSUCCESSFUL_VOTING_MESSAGE, Voting.UIElement.newBuilder()
                .setType(Voting.UIElementDataType.TEXT)
                .setData(ByteString.copyFromUtf8("Voting was unsuccessful. Force restarting."))
                .build());
        systemMessageMap.put(StorageManager.SOMETHING_WRONG_MESSAGE, Voting.UIElement.newBuilder()
                .setType(Voting.UIElementDataType.TEXT)
                .setData(ByteString.copyFromUtf8("Something was terribly wrong. Force restarting."))
                .build());
        systemMessageMap.put(StorageManager.ENCRYPTION_FAILED_MESSAGE, Voting.UIElement.newBuilder()
                .setType(Voting.UIElementDataType.TEXT)
                .setData(ByteString.copyFromUtf8("Encryption failed for some unknown reason."))
                .build());
        systemMessageMap.put(StorageManager.RETRY_BUTTON, Voting.UIElement.newBuilder()
                .setType(Voting.UIElementDataType.TEXT)
                .setData(ByteString.copyFromUtf8("Retry"))
                .build());
        systemMessageMap.put(StorageManager.CANCEL_VOTE_BUTTON, Voting.UIElement.newBuilder()
                .setType(Voting.UIElementDataType.TEXT)
                .setData(ByteString.copyFromUtf8("Cancel Vote"))
                .build());

        Voting.BoothSystemMessages systemMessages = Voting.BoothSystemMessages.newBuilder().putAllSystemMessage(systemMessageMap).build();

        try {
            FileOutputStream output = new FileOutputStream(StorageManagerMockup.systemMessagesFilename);
            systemMessages.writeTo(output);
            output.close();
            System.out.println("Successfully wrote system messages protobuf to a file");
        }
        catch (IOException e) {
            System.err.println("Could not write to the system messages file: '" + StorageManagerMockup.systemMessagesFilename + "'.");
            throw e;
        }

    }
}
