package CipherCracker;

import java.util.*;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;

public class ThreadTask {
    private static final int ALPHABET_SIZE = 26;

    // Pass the actual ciphertext word list
    private static boolean executeConversion(IntList currentKey, ArrayList<Integer> cipherWord, HashSet<IntSequence> validWordsDictionary, int keyOffset) {
        int wordLength = cipherWord.size();
        int[] decryptedBuffer = new int[wordLength];

        for (int i = 0; i < wordLength; i++) {
            int cipherValue = cipherWord.get(i);

            // FIX: Add the offset to i so the key picks up where it left off
            int keyValue = currentKey.getInt((i + keyOffset) % currentKey.size());

            int decryptedValue = (cipherValue - keyValue + ALPHABET_SIZE) % ALPHABET_SIZE;
            decryptedBuffer[i] = decryptedValue;
        }

        // Wrap the decrypted primitive array into a IntSequence
        IntSequence decryptedSequence = new IntSequence(decryptedBuffer);

        // Check if the decrypted word actually exists in the dictionary
        return validWordsDictionary.contains(decryptedSequence);
    }

    public static IntList primaryKeyFinder(HashSet<IntSequence> localCheckSet, ArrayList<Integer> localKeysArray, ArrayList<Integer> localKeysPositions, ArrayList<String> localCipherText) {

        IntArrayList fastLocalKeys = new IntArrayList(localKeysArray);
        IntArrayList fastLocalKeyPositions = new IntArrayList(localKeysPositions);

        int cipherTextLength = localCipherText.size();

        // Guard clause to prevent NullPointerExceptions if the list is empty
        if (cipherTextLength == 0) {
            return null;
        }

        // Fix: Prevent NullPointerException if ciphertext has more than 5 words
        // by restricting processing to the first 5 words.
        if (cipherTextLength > 5) {
            cipherTextLength = 5;
        }

        // Initialize variables to prevent Java compilation errors
        ArrayList<Integer> word4 = null;
        ArrayList<Integer> word3 = null;
        ArrayList<Integer> word2 = null;
        ArrayList<Integer> word1 = null;
        ArrayList<Integer> word0 = null;
        int offset0 = 0;
        int offset1 = 0;
        int offset2 = 0;
        int offset3 = 0;
        int offset4 = 0;

        // Falling-Through Switch (Intentionally omitting breaks)
        switch (cipherTextLength) {
            case (5):
                word4 = ConverterHelpers.primaryIdentifierConverter(localCipherText.get(4));
            case (4):
                word3 = ConverterHelpers.primaryIdentifierConverter(localCipherText.get(3));
            case (3):
                word2 = ConverterHelpers.primaryIdentifierConverter(localCipherText.get(2));
                // fallthrough
            case (2):
                word1 = ConverterHelpers.primaryIdentifierConverter(localCipherText.get(1));
                // fallthrough
            case (1):
                word0 = ConverterHelpers.primaryIdentifierConverter(localCipherText.get(0));
                // fallthrough
        }

        // Move offset calculations outside the loop to improve performance
        switch (cipherTextLength) {
            case (5):
                // FIX: Cumulative size of words 0, 1, 2, and 3
                offset4 = word0.size() + word1.size() + word2.size() + word3.size();
            case (4):
                // FIX: Cumulative size of words 0, 1, and 2
                offset3 = word0.size() + word1.size() + word2.size();
            case (3):
                offset2 = word0.size() + word1.size();
                // fallthrough
            case (2):
                offset1 = word0.size();
                // fallthrough
            case (1):
                // fallthrough
        }

        int numKeys = fastLocalKeyPositions.size();

        for (int i = 0; i < numKeys; i++) {
            int startPos = fastLocalKeyPositions.getInt(i);
            int endPos = (i == numKeys - 1) ? fastLocalKeys.size() : fastLocalKeyPositions.getInt(i + 1);

            // Extract the key candidate
            IntList currentKey = fastLocalKeys.subList(startPos, endPos);

            // Test if this key successfully decrypts all sample words
            if (executeConversion(currentKey, word0, localCheckSet, offset0)) {
                if (cipherTextLength == 1) {
                    return currentKey;
                }
                else if (executeConversion(currentKey, word1, localCheckSet, offset1)) {
                    if (cipherTextLength == 2) {
                        return currentKey;}
                    else if (executeConversion(currentKey, word2, localCheckSet, offset2)) {
                        if (cipherTextLength == 3) {
                            return currentKey;
                        }
                        else if (executeConversion(currentKey, word3, localCheckSet, offset3)) {
                            if (cipherTextLength == 4) {
                                return currentKey;
                            }
                            else if (executeConversion(currentKey, word4, localCheckSet, offset4)) {
                                return currentKey;
                            }
                        }
                    }
                }
            }
        }
        return null;
    }
}