
/***************************************/
/* Student Name: Thomas Huff           */
/*                                     */
/* CMSC 443 Section 0101               */
/*                                     */
/*                                     */
/***************************************/

#define KEY "etaoinshrdlcumwfgypbvkjxqz"  // key from the books
//#define KEY "etaoinhsrdlcumwfgypbvkjxqz"  // my key

/*************************************************************************

     This program was designed to help the user break substitution ciphers.
It displays the text and inables users to swap one letter with another in the
cipher text.  It also has a guessing mode that enables the user to let the
comuter take a guess based on frequency of the letters.  The frequency of
how the letters are supposed to appear the the English laungage is compared
to how the frequency is distributed in the text file that the user is
decrypting.  Then each letter in the cipher text is replaced with the letter
corriponding to the frequency.  After doing this the swap option will enable
the user to swap any two letter and will give the four nearest guesses to the
frequency of the one that you are replacing.  In all likelihood the letter
you choose to swap with your previous choose will be one of this.  This,
however, only works after choosing the guess option.  The user is also 
supplied with an Undo function that
activates the last swap again, therefore undoing it.  A Load funtion, loads
a new file, reset, reset the current file, write saves the file.  And, a
file length and the letter count are also supplied at the top of the screen.
Have fun.

*************************************************************************/

#include <iostream>    // cout
#include <string>      // strcpy
//#include <ctype>       // isprint, islower and tolower
//#include <unistd>      // sleep
#include <cstdio>       // getchar()
#include "io_help.h"     // for opening files

using namespace std;

#define WIDTH 79  // maximum width of screen (allow for vowel count [5 spaces])
#define HEIGHT 18 // maximum height of the screen.  (allow for menu bar and
                                                   // title bar)
#define SIZE (WIDTH * HEIGHT - 1)  // -1 is because array countains the 0 index
#define ORDER_SIZE 26
#define CIPHER "ciphertext"  // encrypted data filename
#define PLAIN "plaintext"  // dectypted data filename

//  This is an array contain the letter that occure most frequently the
//  English language from most to least.
char order[ORDER_SIZE + 1] = {KEY};

//  This array keeps a letter count for each letter (text_freq[0] = a, and
//  contain the number of occurances of 'a' in the 'text' array)
int text_freq[ORDER_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

//  This array keeps the ranking number for the text_freq array (if
//  couversion_table[0] == 2, that means that 'a' has the third highest
//  frequency  [ behind 0 and 1 ofcourse ])
int conversion_table[ORDER_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

char text[SIZE + 1];  // the extra one being for the \0

char input[20];   // filename for input file
char output[20];  // filename for output file

ifstream f_in;    // input filename
ofstream f_out;   // output filename

int objective;        // for encryption or decryption mode.
char choice;          // for main menu choice

char swap1;  // for the undo function
char swap2;  // for the undo function


int array_min (const int array[], int size_of_array)
// get handed an array and the corrisponding size and returns the smallest
// number in the array.
{
    int count;
    int min;

    min = array[0];
    for(count = 1; count < size_of_array; count++)
        if (array[count] < min)
            min = array[count];
    return min;
}

int array_max (const int array[], int size_of_array)
// get handed an array and the corrisponding size and returns the largest
// number in the array.
{
    int count;
    int max;

    max = array[0];
    for(count = 1; count < size_of_array; count++)
        if (array[count] > max)
            max = array[count];
    return max;
}


int getobjective()
// returns 0 for decryption or 1 for encryption
{
    char answer = 'A';  // initilize to any value that is not E e D or d

    cout << "Enter 'E' for Encryption or 'D' for Decryption ---> ";
    cin >> answer;
    cout << endl;
    if (answer == 'E' || answer == 'e')
        return 1;
    if (answer == 'D' || answer == 'd')
        return 0;
    answer = getobjective();
}

void load_new()
// deligates file name and calls the get_file funtion which gets the
// appropriate file.
{
    system ("clear");   // creal screen
    cout << "             Substitution Cipher Encryption/Decryption Kit";
    cout << "        Version 1.0" << endl << endl << endl << endl;
    objective = getobjective();  // get decrypt (0) or encrypt (1) selection
    if (objective)   // set up input and output file names
    {  //encrypt
        strcpy(output, CIPHER);
        strcpy(input, PLAIN);
    }
    else
    {  // decrypt
        strcpy(output, PLAIN);
        strcpy(input, CIPHER);
    }
}

int get_file()
  /* opens reads and closes the file in the input 
     variable and puts it in text  */
  /*  returns the size of 'text'   */
{
    int count;

    OpenInputFile(f_in, input);
    for (count = 0; f_in.peek() != EOF && count <= SIZE; count++)
    {
        f_in.get(text[count]);
        text[count] = tolower(text[count]);
    }
    f_in.close();
    return count;
}

void save_file(int size_of_array)
  /* saves 'text' to the deligated file in the variable: 'output' */
   {
    int count;

    OpenOutputFile(f_out, output);
    for (count = 0; count < size_of_array; count++)
        f_out << text[count];
    f_out.close();
}

// the following function is not used in this program but could be
// implimented very easly.

int vowel_count(int starting_point, int size_of_string)
// takes place to start in the array (text) and number of characters
// and counts the vowels (this number is returned).
{
    int count = ++starting_point;
    int total_vowels = 0;

    for( ; count < (size_of_string + starting_point) ; count++)
       if (text[count] == 'A' || text[count] == 'a' ||
           text[count] == 'E' || text[count] == 'e' ||
           text[count] == 'I' || text[count] == 'i' ||
           text[count] == 'O' || text[count] == 'o' ||
           text[count] == 'U' || text[count] == 'u')
           total_vowels++;
    return total_vowels;
}


void screen(int size_of_file, int length_of_column)
// draws upper part of the screen including the 'text'
{
    int counter;  // for indexing array and counting size
    int end_of_screen;  // to jump to next line if at end of screen
    int letter_count;

    system ("clear");   // clear screen
    cout << "FILE SIZE:";
    cout.width(5);
    cout << size_of_file;
    cout << "  Substitution Cipher Encryption/Decryption Kit   ";
    cout << "LET COUNT:";
    for (counter = 0, letter_count = 0; counter <= SIZE; counter++)
    {
        if (isalpha(text[counter]))
            letter_count++;
    }
    cout.width(5);
    cout << letter_count;
    cout << endl << endl;

    // print out text
    for (counter = 0, end_of_screen = 0; counter < size_of_file; 
         counter++, end_of_screen++)
    {
        cout << text[counter];
        if (text[counter] == '\n')
           end_of_screen = 0;
        if (end_of_screen == length_of_column)
	{
           end_of_screen = 0;
           cout << endl;
	}
    }
    cout << endl;
}


void swap_letters(char first, char sec, int size_of_file)
// switch two letters
{
    int count;

    for (count=0; count < size_of_file; count++)
    {
        if(text[count] == first)
            text[count] = sec;
        else if(text[count] == sec)
            text[count] = first;
    }
}


void move_letters(int size_of_file)
// gets the letters to swap then calls swap_letters with those letters
{
    int order_index;

    screen(size_of_file, WIDTH);
    cout << endl;
    do {
        cout << "Select first letter ---> ";
        cin >> swap1;
    } while (!(isalpha(swap1)));
    swap1 = tolower(swap1);
    for(order_index = 0; swap1 != order[order_index]; order_index++)
        ;
    do {
        cout << "Select second column [";
// for the guesses of which letters would be good guesses
        if (order_index == 0)
        {
            cout << order[order_index+1] << order[order_index+2];
            cout << order[order_index+3] << order[order_index+4];
        }
        else if (order_index == 1)
        {
            cout << order[order_index+1] << order[order_index-1];
            cout << order[order_index+2] << order[order_index+3];
        }
        else if (order_index == ORDER_SIZE - 2)
        {
            cout << order[order_index-1] << order[order_index+1];
            cout << order[order_index-2] << order[order_index-3];
        }
        else
        if (order_index == ORDER_SIZE - 1)
        {
            cout << order[order_index-1] << order[order_index-2];
            cout << order[order_index-3] << order[order_index-4];
        }
        else
        {
            cout << order[order_index-1] << order[order_index+1];
            cout << order[order_index-2] << order[order_index+2];
        }
        cout << "] ---> ";
        cin >> swap2;
    } while (!(isalpha(swap2)));
    swap2 = tolower(swap2);
    swap_letters(swap1, swap2, size_of_file);
}



void guess(int size_of_file)
// This function rewrites for array in memory, to what corrisponds to the
// frequency of the letters in the Enlsih laungage.
{
    int minimum; //smallest number in text_freq, lowest count of any one letter
    int count, count_outside_loop, count_inside_loop;//for loop counter/indexes
    int placement;  // counter kept to keep track of ranking text freqency

// clear tables
    for(count = 0; count < ORDER_SIZE; count++)
    {
        text_freq[count] = 0;
        conversion_table[count] = 0;
    }

// build text_freq
// counts frequency of each letter and puts it in the letters corrisponding
//  array space in text_freq.
    for(count = 0; count < size_of_file; count++)
        if (isalpha(text[count]))
            text_freq[text[count] - 'a']++;


// build conversion table
// ranks text_freq from 0 to ORDER_SIZE, 0 is the letter that appears the most
// ORDER_SIZE is the letter that appears the least.  conversion_table[4] = 0
// that means 'e' appeared at least at many times as any other letter.
    minimum = array_min(text_freq, ORDER_SIZE);
    for(count_outside_loop = array_max(text_freq, ORDER_SIZE), placement = 0;
        count_outside_loop >= minimum; count_outside_loop--)
        for(count_inside_loop = 0; count_inside_loop < ORDER_SIZE;
                                   count_inside_loop++)
            if (text_freq[count_inside_loop] == count_outside_loop)
                conversion_table[count_inside_loop] = placement++;

    for(count = 0; count < size_of_file; count++)
        if (isalpha(text[count]))
            text[count] = order[conversion_table[text[count] - 'a']];
}

void find_freq(int size_of_file)
{
    int count;
    int count_inside_loop, count_outside_loop;
    int minimum, placement;

    system("clear");
    cout << "                         Frequency Distrubution Table";
    cout << endl << endl << endl;


// clear tables
    for(count = 0; count < ORDER_SIZE; count++)
    {
        text_freq[count] = 0;
        conversion_table[count] = 0;
    }

// build text_freq
// counts frequency of each letter and puts it in the letters corrisponding
//  array space in text_freq.
    for(count = 0; count < size_of_file; count++)
        if (isalpha(text[count]))
            text_freq[text[count] - 'a']++;


// build conversion table
// ranks text_freq from 0 to ORDER_SIZE, 0 is the letter that appears the most
// ORDER_SIZE is the letter that appears the least.  conversion_table[4] = 0
// that means 'e' appeared at least at many times as any other letter.
    minimum = array_min(text_freq, ORDER_SIZE);
    for(count_outside_loop = array_max(text_freq, ORDER_SIZE), placement = 0;
        count_outside_loop >= minimum; count_outside_loop--)
        for(count_inside_loop = 0; count_inside_loop < ORDER_SIZE;
                                   count_inside_loop++)
            if (text_freq[count_inside_loop] == count_outside_loop)
                conversion_table[count_inside_loop] = placement++;

// print the key
    cout << "             Frequency order in the English laungage ";
    cout << "(The KEY)" << endl << endl;
    for (count = 0; count < ORDER_SIZE; count++)
    {
        cout << "  " << order[count];
    }
    cout << endl << endl << endl;

// from most to least
    cout << "           Frequency Distribution from most to least ---------->";
    cout << endl << endl; 
    for(count_outside_loop = 0; count_outside_loop < ORDER_SIZE; 
        count_outside_loop++)
        for(count_inside_loop = 0; count_inside_loop < ORDER_SIZE; 
            count_inside_loop++)
            if(conversion_table[count_inside_loop] == count_outside_loop)
                cout << (char)(count_inside_loop + 'a') << " = " <<
                    text_freq[count_inside_loop] << "\t";
    cout << endl << endl << endl;
// in alphabetical order
    cout << "           Frequency Distribution in alphabetical order ------->";
    cout << endl << endl; 
    for(count = 0; count < ORDER_SIZE; count++)
         cout << (char)(count + 'a') << " = " << text_freq[count] << "\t";
    cout << endl << endl << endl;
    cout << "                                press return" << endl;
    getchar();  // gets return

}

void help()
// prints help screen on the screen
{
    system ("clear");   // clear screen
    cout << "                 Substitution Cipher Encryption/Decryption Kit";
    cout << endl;
    cout << "                                HELP SCREEN" << endl << endl;

    cout << "Guess          -- Overlays a histogram of frequency distrabution";
    cout << " and replaces" << endl;
    cout << "                  matrix with best choices" << endl;
    cout << "Swap letter    -- Interchanges two letters and provides good ";
    cout << "guesses for" << endl;
    cout << "                  the substitution" << endl;
    cout << "Undo last move -- Take back your last move (use reset to take ";
    cout << "back guess)" << endl;
    cout << "Frequency table-- Letter frequency count, in alphabetical and ";
    cout << "frequential order" << endl;
    cout << "Reset          -- Reset the matrix you are currently working on";
    cout << endl;
    cout << "Load           -- Let you reselect loading option" << endl;
    cout << "Write          -- Writes output to the output file" << endl;
    cout << "Exit           -- Leaves the program (and prompt for save)"<<endl;
    cout << endl;
    cout << "GENERAL" << endl << "-------" << endl;
    cout << "- The file size and total character count appear at ";
    cout << "the top" << endl;
    cout << "- Best guesses for a substitution are provided between brakets ";
    cout << "on swap screen" << endl;
    cout << "- If you choose not to substitute a letter after starting, just ";
    cout << "substitute a" << endl;
    cout << "  letter with itself" << endl;
    cout << "- To select one of the above options select the Capitolized ";
    cout << "letter" << endl;
    cout << "  from the menu" << endl;
    cout << "                                press return" << endl;
    getchar();  // gets return
}

main()
{
    int size_of_file;   // size of the text file

    load_new();
    size_of_file = get_file();

    choice = 'A';  // put anything in choice except 'E' or 'e'
    while ((choice != 'E') && (choice != 'e')) // while choice != exit code
    {
        screen(size_of_file, WIDTH);
        cout << endl;
        cout << "Guess                Swap letters         ";
        cout << "Undo last swap       Frequency tables" << endl;
        cout << "Reset                Load                 ";
        cout << "Write                Help" << endl;
        cout << "Action To Pursue ['E' to Exit] ---> ";
        cin >> choice;
        if (choice == 'G' || choice == 'g') // Guess
            guess(size_of_file);
        else if (choice == 'S' || choice == 's') // Swap letters
            move_letters(size_of_file);
        else if (choice == 'U' || choice == 'u') // Undo last swap
            swap_letters(swap1, swap2, size_of_file);
        else if (choice == 'F' || choice == 'f') // show frequeny tables
            find_freq(size_of_file);
        else if (choice == 'R' || choice == 'r') // Reset This Cipher
            size_of_file = get_file();
        else if (choice == 'L' || choice == 'l') // Load New Cipher
        {
            load_new();
            size_of_file = get_file();
	}
        else if (choice == 'W' || choice == 'w') // Write Cipher to f_out
            save_file(size_of_file);
        else if (choice == 'H' || choice == 'h') // Help
            help();
        else if (choice != 'E' && choice != 'e') // if not exit
	{
            system("clear");
            cout <<endl<<endl<<endl<<endl<<endl<<endl<<endl<<endl<<endl;
            cout << "                             Not A Valid Command" << endl;
            sleep(2);
        }
    }
    cout << "Save file before exiting (y/n) ---> ";
    cin >> choice;
    if (choice == 'Y' || choice == 'y') // save file
        save_file(size_of_file);
    cout << endl;

}  // main()



