package hcrypto.analyzer;

import hcrypto.cipher.*;

public class FrequencyTable {

    public static final int MAX_CHARS = 128;      // Possibly all ASCII characters
    public FrequencyRecord frequencies[] = new FrequencyRecord[MAX_CHARS];    
    private FrequencyRecord sortedFrequencies[] = new FrequencyRecord[frequencies.length];    
    private int alphabetRange = Alphabet.RANGE_ascii;
    
    private String text;
    Alphabet alphabet = null;
    public int histogram[] = new int[MAX_CHARS];
    private int highestFreq;                      // Highest f for bar chart
    private int arraySize;

    private int charCount = 0;       
    private int alphabeticsCount = 0;   

    public FrequencyTable(String text) {
//       for (int k = 0; k < frequencies.length; k++) {
//           frequencies[k] = new FrequencyRecord((char)k, 0);
//       }
       this.text = text;
       count(text);
//       sort(); 
//       print();   
    }

    public void count(String text) {
        alphabet = null;
        
//        int size = calculateArraySize();
        int size = MAX_CHARS;
        frequencies = new FrequencyRecord[size];    
        sortedFrequencies = new FrequencyRecord[frequencies.length];  
        for (int k = 0; k < frequencies.length; k++)
            frequencies[k] = new FrequencyRecord((char)k, 0);
        
        try {  
            alphabet = new Alphabet(alphabetRange);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        charCount = 0;
        alphabeticsCount = 0;
        highestFreq = 0;
        
        for (int k = 0; k < text.length(); k++) {
            char ch = text.charAt(k);
            if (Character.isLetter(ch))
                 ++alphabeticsCount;
            if (alphabet.isInAlphabet(ch)) { // < MAX_CHARS) {       // Ignore chars > 127
                charCount++;
                frequencies[ch].count++;
                if (frequencies[ch].count > highestFreq) 
                    highestFreq = frequencies[ch].count; // Keep track of highest f
            }
        }  
        sort();  
    }
    
    
    /**
     *  The coincidence index (CI) is a measure of the flatness
     *  of the frequency histogram for the file. This formula
     *  is derived in Beker and Piper, p 41 and also Sinkov, p. 68.
     *  It can be used to distinguish mono- and poly-alphabetic ciphers.
     *  Rules: For plaintext and monoalphabetic ciphers, the CI is around 0.065
     *         A completely flat histogram would be around 0.038
     */
    public double computeCoincidenceIndex() {
//        int alphabeticsCount = freqTable.getAlphabeticsCount();
        double sumFreqs = 0.0;
//        for (int k = 0; k < 26 ; k++) {
        for (int k = 0; k < frequencies.length ; k++) {
//            double freq = getCount('a' + k) + getCount('A' + k);
            double freq = frequencies[k].count;
            sumFreqs = sumFreqs + freq * (freq - 1.0);
        }
//         return sumFreqs / (alphabeticsCount * (alphabeticsCount - 1.0));
       return sumFreqs / (charCount * (charCount - 1.0));
    }


    public void setAlphabetRange(int range) {
        if (alphabetRange == range) 
            return;
        alphabetRange = range;
        count(text);
//        print();
    }

    /**
     * returns the the kth highest frequency character
     */
    public String getKHighestFreqChar (int k) {
        return charToString(sortedFrequencies[k-1].ch);
    }

    public int getHighestFreq() {
        return highestFreq;
    }
    
    public int[] getFrequencies() {
        return getHistogramData();
    }

    public int[] getHistogramData() {
//        print();
        for (int k = 0; k < histogram.length; k++) 
            histogram[k] = frequencies[k].count;
        return histogram;    
    }
    
    public void print() {
//        java.text.NumberFormat nf = java.text.NumberFormat.getNumberInstance();
//        nf.setMinimumIntegerDigits(4); 
        System.out.println("\n\n");
        for (int k = 0; k < frequencies.length; k++) {
            System.out.print(charToString(frequencies[k].ch) + ":" + frequencies[k].count + " ");
            if ((k + 1) % 10 == 0)
                System.out.println();
        }
        System.out.println();
    }
    
    protected String charToString(char ch) {
        if (Character.isLetterOrDigit(ch))
            return ch + "";
        else
            return "\\" + (int)ch;

    }

    public int getCharCount() {
        return charCount;
    }
    
    public int getAlphabeticsCount() {
        return alphabeticsCount;
    }

    public int getCount(int ch) {
        return frequencies[ch].count;
    }

    /**
     * Does a selection sort of an array of FrequencyRecords, 
     *  returning a new array.
     */
//    protected FrequencyRecord[] sort(FrequencyRecord freqs[]) {
    protected void sort() {
//        sortedFrequencies[] = new FrequencyRecord[frequencies.length];
        for (int k = 0; k < frequencies.length; k++)
            sortedFrequencies[k] = new FrequencyRecord(frequencies[k]);
     
        FrequencyRecord tempRec;
        for (int k = 0; k < sortedFrequencies.length; k++) {
            int maxCount = k;	// maxCount points to largest so far
            for (int j = k+1; j < sortedFrequencies.length; j++)	 
                if (sortedFrequencies[j].count > sortedFrequencies[maxCount].count) 
                    maxCount = j;
            tempRec = sortedFrequencies[maxCount];    // swap maxCount and k records
            sortedFrequencies[maxCount] = sortedFrequencies[k];
            sortedFrequencies[k] = tempRec;
	    } 
    }


}