package meerkat.voting;

import com.google.protobuf.ByteString;
import meerkat.crypto.DigitalSignature;
import meerkat.crypto.Encryption;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.*;
import meerkat.voting.output.*;
import meerkat.voting.storage.*;
import meerkat.voting.encryptor.*;
import meerkat.voting.gui.ui.*;

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

/**
 * Created by hai on 26/04/16.
 */
public class VotingBoothToyRun {

    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();
        SystemConsoleOutputDevice outputDevice = new SystemConsoleOutputDevice();
        VBCryptoManager cryptoManager = new VBCryptoManagerImpl(rand, enc, sig);
        SystemConsoleUI ui = new SystemConsoleUI ();


        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 {

        ElectionParams electionParams = 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<BallotQuestion> generateChannelChoiceQuestions() {
        ArrayList<BallotQuestion> channelChoiceQuestions = new ArrayList<>();

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

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

        return channelChoiceQuestions;
    }


    private static List<BallotQuestion> generateBallotQuestions() {
        ArrayList<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 BallotQuestion generateBallotQuestion(String questionStr, String descriptionStr, String[] answers) {
        UIElement question = UIElement.newBuilder()
                .setType(UIElementDataType.TEXT)
                .setData(stringToBytes(questionStr))
                .build();

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

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

        return bqb.build();
    }


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

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

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

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

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

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

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

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

        return 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, UIElement> systemMessageMap = new HashMap<>();

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

        BoothSystemMessages systemMessages = 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;
        }

    }
}
