package simulator;

import java.util.ArrayList;

public class Main {

    private static void exit(String error) {
        System.out.println(error);
        System.out.println("Usage: ");
        System.out.println("1) To encrypt: java -jar tunny.jar -e <key filename> -i <plaintext filename>");
        System.out.println("   <plaintext filename> contains the plaintext in British notation.");
        System.out.println("2) To decrypt: java -jar tunny.jar -d <key filename> -i <ciphertext filename>");
        System.out.println("   <ciphertext filename> contains the ciphertext in British notation.");
        System.out.println("3) To run a virtual Colossus machine: java -jar tunny.jar -c <key filename> -i <ciphertext filename>");
        System.out.println("   <key filename> must contain at least the first two lines with the CHI1 and CHI2 wheel patterns.");
        System.out.println("4) To create simulated challenges: java -jar tunny.jar -s <level> -b <book filename> [-b <additional book filename> [-b <additional book filename> ....]");
        System.out.println("   <level> challenge level, 2 or 3.");
        System.out.println("   <book filename> a textfile with normal text.");

        System.exit(-1);
    }
    public static void main(String[] args) {

        if (args.length == 0) {
            exit("No command specified");
        }

        ArrayList<String> bookFiles = new ArrayList<>();
        String encryptionKeyFile = "";
        String decryptionKeyFile = "";
        String colossusKeyFile = "";
        String inputFile = "";
        int simulationLevel = -1;

        int argIndex = 0;
        while (argIndex < args.length) {
            String arg = args[argIndex];
            argIndex++;
            boolean isLast = argIndex == args.length;
            if (arg.equalsIgnoreCase("-e")) {
                if (isLast) {
                    exit("Missing key filename after -e");
                }
                if (encryptionKeyFile.isEmpty()) {
                    encryptionKeyFile = args[argIndex];
                } else {
                    exit("-e (key filename) specified twice");
                }
                continue;
            }
            if (arg.equalsIgnoreCase("-d")) {
                if (isLast) {
                    exit("Missing key filename after -d");
                }
                if (decryptionKeyFile.isEmpty()) {
                    decryptionKeyFile = args[argIndex];
                } else {
                    exit("-d (key filename) specified twice");
                }
                continue;
            }
            if (arg.equalsIgnoreCase("-c")) {
                if (isLast) {
                    exit("Missing key filename after -c");
                }
                if (colossusKeyFile.isEmpty()) {
                    colossusKeyFile = args[argIndex];
                } else {
                    exit("-c (key filename) specified twice");
                }
                continue;
            }
            if (arg.equalsIgnoreCase("-i")) {
                if (isLast) {
                    exit("Missing input file after -i");
                }
                if (inputFile.isEmpty()) {
                    inputFile = args[argIndex];
                } else {
                    exit("-i (input file) specified twice");
                }
                continue;
            }
            if (arg.equalsIgnoreCase("-b")) {
                if (isLast) {
                    exit("Missing argument after -b");
                }
                bookFiles.add(args[argIndex]);
                continue;
            }
            if (arg.equalsIgnoreCase("-s")) {
                if (isLast) {
                    exit("Missing level after -s");
                }
                if (simulationLevel == -1) {
                    simulationLevel = Integer.valueOf(args[argIndex]);
                    if (simulationLevel != 2 && simulationLevel != 3){
                        exit("Invalid simulation level (expecting 2 or 3) " + args[argIndex]);
                    }
                } else {
                    exit("-s (simulation) specified twice");
                }
            }

        }
        if (simulationLevel != -1) {
            if (!colossusKeyFile.isEmpty()) {
                exit("-c not compatible with -s");
            }
            if (!encryptionKeyFile.isEmpty()) {
                exit("-e not compatible with -s");
            }
            if (!decryptionKeyFile.isEmpty()) {
                exit("-d not compatible with -s");
            }
            if (!inputFile.isEmpty()) {
                exit("-i not compatible with -s");
            }
            if (bookFiles.isEmpty()) {
                exit("-b (book files) not specified");
            }
            String[] files = new String[bookFiles.size()];
            bookFiles.toArray(files);
            Reference.readBooks(files);


            int challengeNumber = 1;
            int level;

            if (simulationLevel == 2) {
                level = 2;
                Simulation.depthChallenge(challengeNumber++, level, ChallengeType.DEPTH, 500, 8, Limitation.NO_LIMITATION);
                Simulation.depthChallenge(challengeNumber++, level, ChallengeType.DEPTH, 500, 4, Limitation.CHI2_1BACK);
                Simulation.depthChallenge(challengeNumber++, level, ChallengeType.DEPTH, 800, 2, Limitation.PSI1_1BACK);
                Simulation.challenge(challengeNumber++, level, ChallengeType.SETTING, ChallengeSubType.FIND_CHI12, 3000, Limitation.CHI2_1BACK, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.SETTING, ChallengeSubType.FIND_CHI12, 3000, Limitation.PSI1_1BACK, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.SETTING, ChallengeSubType.FIND_CHI, 5000, Limitation.NO_LIMITATION, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.SETTING, ChallengeSubType.FIND_PSI_KNOWN_CHI_MU, 2000, Limitation.NO_LIMITATION, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.SETTING, ChallengeSubType.FIND_MU_KNOWN_CHI_PSI, 2000, Limitation.NO_LIMITATION, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.SETTING, ChallengeSubType.FIND_MU_PSI_KNOWN_CHI, 5000, Limitation.NO_LIMITATION, 21);

                Simulation.challenge(challengeNumber++, level, ChallengeType.KEY_BREAKING, ChallengeSubType.FIND_CHI12, 500, Limitation.NO_LIMITATION, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.KEY_BREAKING, ChallengeSubType.FIND_CHI12, 500, Limitation.CHI2_1BACK, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.KEY_BREAKING, ChallengeSubType.FIND_PSI_KNOWN_CHI_MU, 1000, Limitation.NO_LIMITATION, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.KEY_BREAKING, ChallengeSubType.FIND_MU_KNOWN_CHI_PSI, 1000, Limitation.NO_LIMITATION, 21);
            } else {
                level = 3;
                Simulation.challenge(challengeNumber++, level, ChallengeType.SETTING, ChallengeSubType.FIND_ALL, 5000, Limitation.NO_LIMITATION, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.SETTING, ChallengeSubType.FIND_ALL, 5000, Limitation.CHI2_1BACK, 21);

                Simulation.challenge(challengeNumber++, level, ChallengeType.KEY_BREAKING, ChallengeSubType.FIND_CHI, 1500, Limitation.NO_LIMITATION, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.KEY_BREAKING, ChallengeSubType.FIND_MU_PSI_KNOWN_CHI, 1500, Limitation.NO_LIMITATION, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.KEY_BREAKING, ChallengeSubType.FIND_ALL, 3000, Limitation.NO_LIMITATION, 21);

                Simulation.challenge(challengeNumber++, level, ChallengeType.BREAKING, ChallengeSubType.FIND_CHI12, 15000, Limitation.CHI2_1BACK, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.BREAKING, ChallengeSubType.FIND_CHI, 15000, Limitation.NO_LIMITATION, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.BREAKING, ChallengeSubType.FIND_PSI_KNOWN_CHI_MU, 15000, Limitation.NO_LIMITATION, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.BREAKING, ChallengeSubType.FIND_MU_KNOWN_CHI_PSI, 15000, Limitation.NO_LIMITATION, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.BREAKING, ChallengeSubType.FIND_MU_PSI_KNOWN_CHI, 15000, Limitation.NO_LIMITATION, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.BREAKING, ChallengeSubType.FIND_MU_PSI_KNOWN_CHI, 15000, Limitation.CHI2_1BACK, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.BREAKING, ChallengeSubType.FIND_MU_PSI_KNOWN_CHI, 25000, Limitation.P5_2BACK, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.BREAKING, ChallengeSubType.FIND_ALL, 15000, Limitation.NO_LIMITATION, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.BREAKING, ChallengeSubType.FIND_ALL, 25000, Limitation.CHI2_1BACK, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.BREAKING, ChallengeSubType.FIND_ALL, 25000, Limitation.PSI1_1BACK_P5_2BACK, 21);
                Simulation.challenge(challengeNumber++, level, ChallengeType.BREAKING, ChallengeSubType.FIND_ALL, 25000, Limitation.P5_2BACK, 21);
            }






        } else {
            if (!bookFiles.isEmpty()) {
                exit("-b (book files) not compatible with -k");
            }

            if (!encryptionKeyFile.isEmpty() && !decryptionKeyFile.isEmpty()) {
                exit("-e (encryption key) and -d (decryption key) cannot both be specified");
            }
            if (!encryptionKeyFile.isEmpty() && !colossusKeyFile.isEmpty()) {
                exit("-e (encryption key) and -c (Colossus key) cannot both be specified");
            }
            if (!colossusKeyFile.isEmpty() && !decryptionKeyFile.isEmpty()) {
                exit("-c (Colossus key) and -d (decryption key) cannot both be specified");
            }
            if (inputFile.isEmpty()) {
                exit("-i (input file) not specified");
            }
            Tunny t = new Tunny();
            byte[] input = Baudot.readBritishNotationFile(inputFile);

            if (!encryptionKeyFile.isEmpty()) {
                t.parse(Utils.readFile(encryptionKeyFile));

                byte[] cipher = new byte[input.length];

                t.encipher(cipher, input.length, input, true);
                t.print(false);
                System.out.println(Baudot.toBritishNotation(input));
                System.out.println(Baudot.toBritishNotation(cipher));

            } else if (!decryptionKeyFile.isEmpty()) {
                t.parse(Utils.readFile(decryptionKeyFile));

                byte[] plain = new byte[input.length];

                t.encipher(plain, input.length, input, false);
                t.print(false);
                System.out.println(Baudot.toBritishNotation(input));
                System.out.println(Baudot.toBritishNotation(plain));

            } else if (!colossusKeyFile.isEmpty()) {
                System.out.println(Baudot.toBritishNotation(input));
                System.out.printf("Ciphertext length: %d\n", input.length);
                t.parse(Utils.readFile(colossusKeyFile));
                System.out.println(Patterns.toString(t.patterns.chi[Tunny.CHI1]));
                System.out.println(Patterns.toString(t.patterns.chi[Tunny.CHI2]));
                Colossus.runDoubleDelta12(t, input, input.length);
            } else {
                exit("Neither -e (encryption key) nor -d (decryption key) nor -c (key for Colossus) is specified");
            }



        }







    }


}
