package edu.caltech.cs2.project01;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class SubstitutionCipher {
    private String ciphertext;
    private Map<Character, Character> key;

    // Use this Random object to generate random numbers in your code,
    // but do not modify this line.
    private static final Random RANDOM = new Random();

    /**
     * Construct a SubstitutionCipher with the given cipher text and key
     * @param ciphertext the cipher text for this substitution cipher
     * @param key the map from cipher text characters to plaintext characters
     */
    public SubstitutionCipher(String ciphertext, Map<Character, Character> key) {
        this.ciphertext = ciphertext;
        this.key = key;
    }

    /**
     * Construct a SubstitutionCipher with the given cipher text and a randomly
     * initialized key.
     * @param ciphertext the cipher text for this substitution cipher
     */
    public SubstitutionCipher(String ciphertext) {
        this.ciphertext = ciphertext;
        Map<Character, Character> key1 = new HashMap<Character, Character>();
        for(int i = 'A'; i <= 'Z'; i++){
            key1.put((char) i, (char) i);
        }
        this.key = key1;
        for(int i = 0; i < 10000; i++){
            this.key =  this.randomSwap().key;
        }
    }

    /**
     * Returns the unedited cipher text that was provided by the user.
     * @return the cipher text for this substitution cipher
     */
    public String getCipherText() {
        return ciphertext;
    }

    /**
     * Applies this cipher's key onto this cipher's text.
     * That is, each letter should be replaced with whichever
     * letter it maps to in this cipher's key.
     * @return the resulting plain text after the transformation using the key
     */
    public String getPlainText() {
        String result = "";
        for(int i = 0; i < this.ciphertext.length(); i++){
            result += key.get(this.ciphertext.charAt(i));
        }
        return result;
    }

    /**
     * Returns a new SubstitutionCipher with the same cipher text as this one
     * and a modified key with exactly one random pair of characters exchanged.
     *
     * @return the new SubstitutionCipher
     */
    public SubstitutionCipher randomSwap() {
        this.ciphertext = ciphertext;
        char swap1 = (char) RANDOM.nextInt(65, 91);
        char swap2 = (char) RANDOM.nextInt(65, 91);
        Map<Character, Character> newKey = new HashMap<Character, Character>();

        while(swap1 == swap2){
            swap1 = (char) RANDOM.nextInt(65, 91);
        }
        for(char c : key.keySet()){
                newKey.put(c, c);

        }

        newKey.put(swap1, key.get(swap2));
        newKey.put(swap2, key.get(swap1));

         return new SubstitutionCipher(this.ciphertext, newKey);
    }

    /**
     * Returns the "score" for the "plain text" for this cipher.
     * The score for each individual quadgram is calculated by
     * the provided likelihoods object. The total score for the text is just
     * the sum of these scores.
     * @param likelihoods the object used to find a score for a quadgram
     * @return the score of the plain text as calculated by likelihoods
     */
    public double getScore(QuadGramLikelihoods likelihoods) {
        double result = 0.0;
        String test = getPlainText();
        for(int i = 0; i < test.length() - 3; i++){
            result += likelihoods.get(test.substring(i, i+4));
        }
        return result;
    }

    /**
     * Attempt to solve this substitution cipher through the hill
     * climbing algorithm. The SubstitutionCipher this is called from
     * should not be modified.
     * @param likelihoods the object used to find a score for a quadgram
     * @return a SubstitutionCipher with the same ciphertext and the optimal
     *  found through hill climbing
     */
    public SubstitutionCipher getSolution(QuadGramLikelihoods likelihoods) {
        return null;
    }
}
