/* File 	: trans.c
 * Author 	: B. EKPERIGIN
 * Created	: March 6 1998
 * Last Revised : March 27 1998
 *
 * Program to aid in breaking simple transposition ciphers
 * Modified program originally written by a past CMSC 443 student
 * I added functionality to 
 *     - read data into a specified rectangle size by columns
 *       and by rows (instead of by rows only)
 *     - permute rows and columns (instead of column permuting only)
 *     - write rectangle to file by columns and by rows (instead
 *       of by rows only)
 *     - Undo the last N permutations where N is any positive integer
 *       in the range 0-#of previous permutations. The original 
 *       program only supported undo of the last permutation
 *
 * To run the program after compiling, type
 *      <executable> <input file> <output file>
 * at the command prompt.
 */ 

#include "mytrans.h"

/**************************************************************************
 * Function - main
 * Command line arguments are the input file containing the ciphertext to 
 * be deciphered, and the output file to write the resulting plaintext
 *  - Opens input and output files, if error exit.
 *  - Displays options to user and processes requests until user exits.
 */
main(int argc,char* argv[])
{
   int x, danswer;                                      

   if ( argc != 3 ) {
      printf("Usage: sts message_file output_file\n");
      exit(0);
   }
   if ((ifp = fopen(argv[1],"r")) == NULL ) {
      printf("Input (ciphertext) file %s does not exist. \n",argv[1]);
      exit(2);
   }
   if ((ofp = fopen(argv[2],"w")) == NULL ) {
      printf("Cannot open output file.\n");
      exit(3);
   }

   message_length = ReadMessage();

   printf("\nWelcome to the Simple Transposition Cipher Solver.\n\n");
   PrintMainMenu();
   printf("Enter a command --> ");

   while ( scanf("%d",&danswer) > 0 ) {
      ProcessMainRequest(danswer);     
      PrintMainMenu();
      printf("Enter a command --> ");
   }
}

/************************************************************************
 * Function - ProcessMainRequest
 * Processes the user response to the main menu
 */
void ProcessMainRequest(int danswer) {

   int width; 

   switch(danswer) {

      case 1 : printf("\n   Enter a valid rectangle width --> ");
               scanf("%d",&width);
               if((message_length % width) != 0) {
                 printf("   Using default width of 50.\n");
                 width = 50;
               }
               PrintOrigCipher(message, width);
     	       break;

      case 2 : printf("\nThere are %i characters in the message.\n\n",
                message_length);
      	       break;

      case 3 : PrintRectangleSizes();
               break;

      case 4 : PrintVowelCount();
               break;

      case 5 : printf("\n   Entering solving mode...\n");
               SolvingMode();
	       break;
     
      case 6 : printf("Program exiting...\n\n");
               exit(5);
 
      default : break;

   }
}

/*******************************************************************
 * Function - ReadMessage
 * Reads the ciphertext from the user-specified input file into the 
 * message array
 */

int ReadMessage()
{
  int pos=0,nch;

  while( ((nch=getc(ifp))!=EOF) && pos<MSGLEN ) {
    if(nch>=32 && nch<128) message[pos++]=nch;
  }

  message[pos]='\0';
  return pos;
}

/*******************************************************************
 * Function - PrintMainMenu
 * Displays the main menu options to the user
 */
void PrintMainMenu()
{
   printf("             Main Menu Options\n");
   printf("             -----------------\n");
   printf("     1) Display the cipher text.\n");
   printf("     2) Count the total number of characters.\n");
   printf("     3) Print possible rectangle sizes.\n");
   printf("     4) Print vowel count by row.\n");
   printf("     5) Enter solving mode.\n");
   printf("     6) Quit.\n\n");
}

/************************************************************************
 * Function - PrintSolveMenu
 * Print the solving mode menu to the screen
 */

void PrintSolveMenu()
{
   printf("             Solving Mode Options\n");
   printf("             --------------------\n");
   printf("    1) Permute two columns of text.\n");
   printf("    2) Permute two rows of text.\n");
   printf("    3) Undo the last N permutations.\n");
   printf("    4) Undo all permutations (Start over).\n");
   printf("    5) Print the current rectangle to output file by rows.\n");
   printf("    6) Print the current rectangle to output file by columns.\n");
   printf("   10) Return to main menu.\n\n");
}

/************************************************************************
 * Function - PrintToFileByRows
 * Print the current rectangle to the output file one row at 
 * a time (topmost column to bottommost column)
 */

void PrintToFileByRows(char** rect, int rows, int cols)
{
 int i, j;

 for(i=0; i<rows; i++) {
   for(j=0; j<cols; j++) {
    fprintf(ofp, "%c", rect[i][j]);
   }
   fprintf(ofp, "\n");
 }

}

/********************************************************
 * Function - PrintToFileByColumns
 * Print the current rectangle to the output file one column at 
 * a time (leftmost column to rightmost column)
 */

void PrintToFileByColumns(char** rect, int rows, int cols) 
{
 int i, j;

 for(i=0; i<cols; i++) {
  for(j=0; j<rows; j++) {
    fprintf(ofp, "%c", rect[j][i]);
  }
  fprintf(ofp, "\n");
 }

}

/********************************************************************************
 * Function - PrintOrigCipher
 * Print the original cipher to the screen
 */

void PrintOrigCipher(char msg[], int width)
{
   int x;                             

   printf("\n   Cipher text on a width of %d\n\n   ",width);
   if ( width > 9 ) {
      printf("         ");
      for ( x = 10; x <= width; x++ ) {
         printf("%i",(x % 100) / 10); 
      }
      printf("\n   ");
   } 
   for ( x = 1; x <= width; x++ ) {
      printf("%d",x % 10);
   }
   printf("\n   ");
   for ( x = 1; x <= width; x++ ) {
      printf("-");
   }
   printf("\n   ");
   for ( x = 0; x < message_length; x++ ) {
      if ( x > 0 && ( x % width ) == 0 ) {
         printf("\n   ");
      }
      printf("%c",msg[x]);
   }
   printf("\n\n");
} 

/*******************************************************************
 * Function - SolvingMode
 * Enter solving mode. In this mode, the user is given the option
 * to modify the rectnagle (ciphertext) eg by permuting rows and
 * columns.
 */

void SolvingMode()
{
   int columns = 0, fillmode, rows = 0, danswer, x;                       
   char** currentRect;

   printf("\n   Enter a valid rectangle width -- > ");
   scanf("%d",&columns);
   while ( ( message_length % columns) != 0 ) {
      printf("   Invalid rectangle width %d.\n",danswer);
      printf("\n   Enter a valid rectangle width --> ");
      scanf("%d",&columns);
   }
   rows = message_length / columns;

   printf("\n1. Fill rectangle by rows\n");
   printf("2. Fill rectangle by columns\n");
   printf("\nEnter your choice -> ");
   scanf("%d", &fillmode);
  
   currentRect = (char**) malloc(sizeof(char*) * rows);
   for (x=0; x<rows; x++) currentRect[x] = (char*) malloc(sizeof(char) * columns);

   if(fillmode==1) FillRect(currentRect, rows, columns, 'r');
   if(fillmode==2) FillRect(currentRect, rows, columns, 'c');

   PrintRect(currentRect,rows, columns);
   PrintSolveMenu();
   printf("Enter a command --> ");

   while ( scanf("%d",&danswer) > 0 ) {

      if(danswer == 10) {
	 for(x=0; x<rows; x++) free(currentRect[x]);
         free(currentRect);
         printf("\n   Returning to main menu...\n\n");  
         return;
      }
      else ProcessSolveRequest(currentRect, danswer, rows, columns, fillmode);
      PrintRect(currentRect, rows, columns);
      PrintSolveMenu();
      printf("\nEnter a command --> ");
   }    
} 

/*****************************************************************
 * Function - FillRect
 * this function fills the working rectangle with the original
 * ciphertext.
 */

void FillRect(char** rect, int rows, int columns, char fillmode)
{
 int i, j;

 if(fillmode == 'r') {
   for(i=0; i<rows; i++) {
     for(j=0; j<columns; j++) {
       rect[i][j] = message[i*columns+j];
     }
   }
 }

 if(fillmode == 'c') {
   for(i=0; i<columns; i++) {
     for(j=0; j<rows; j++) {
       rect[j][i] = message[i*rows+j];
     }
   }
 }

}

/*******************************************************************
 * Function - ProcessSolveRequest
 * Processes user response to the solving mode menu
 */

void ProcessSolveRequest(char** currentRect, int ans, int rows, int
columns, int fillmode) 
{
   int swap1=-1, swap2=-1, x, perms;
   char type;

   switch(ans) {

      case 1 : while (1) {
                 printf("\n   Enter one column number --> ");
                 scanf("%d",&swap1);
                 printf("   Enter the second column number --> ");
                 scanf("%d",&swap2);
		 if(swap1>=1 || swap1<=columns || swap2>=1 || swap2<=columns) break;
		 else printf("   Invalid column number given.\n");
               }
               SwapColumns(currentRect, swap1,swap2,rows,columns);
               break;
     
      case 2 : while (1) {
                 printf("\n   Enter one row number --> ");
                 scanf("%d",&swap1);
                 printf("   Enter the second row number --> ");
                 scanf("%d",&swap2);
		 if(swap1>=1 || swap1<=rows || swap2>=1 || swap2<=rows) break;
		 else printf("   Invalid row number given.\n");
               }
               SwapRows(currentRect, swap1,swap2,rows,columns);
               break;

      case 3 : printf("Undo the last N permutations...Enter the value for N: ");
               scanf("%d", &perms);
               printf("\n   Undoing the previous %d permutations...\n\n", perms);
               UndoLastNPermutations(perms, currentRect, rows, columns);
               break;
 
      case 4 : printf("\n   Erasing all previous permutations...\n\n");
               FillRect(currentRect, rows, columns, fillmode);
               swap_pointer = 0;
               break;

      case 5 : printf("\nPrinting current rectangle to output file by rows...\n\n");
               PrintToFileByRows(currentRect, rows, columns);
	       break;

      case 6 : printf("\nPrinting current rectangle to output file by columns...\n\n");
	       break;

   }
 
}

/**************************************************************************
 * Function - UndoLastNPermutations
 * This function undoes the last N permutations (either row or column
 * permutations) where N is the number of permutations to undo. N
 * should be between 0 and #of previous permutations.
 */
void UndoLastNPermutations(int num, char** currentRect, int rows, int columns)
{
  int i, swap1, swap2;
  char type;

  for(i=0; i<num; i++) {
   if (swap_pointer>0) {
     swap_pointer--;
     swap1 = swaps[swap_pointer].val1;
     swap2 = swaps[swap_pointer].val2;
     type = swaps[swap_pointer].type;
     if(type=='c'){
       SwapColumns(currentRect, swap1,swap2,rows,columns);
     }
     if(type=='r') {
       SwapRows(currentRect, swap1, swap2, rows, columns);
     }
     swap_pointer--;
    }
    else {
     printf("\n   No previous permutations to undo...\n\n");
     break;
    }
  }
}

/*******************************************************************
 * Function - SwapColumns
 * Swap two columns of the ciphertext
 */

void SwapColumns(char** rect, int s1, int s2, int r, int c)
{
   int x;
   char temp;

   for (x=0; x<r; x++) {
      temp = rect[x][s1-1];
      rect[x][s1-1] = rect[x][s2-1];
      rect[x][s2-1] = temp;
   }
   swaps[swap_pointer].val1 = s1;
   swaps[swap_pointer].val2 = s2;
   swaps[swap_pointer].type = 'c';
   swap_pointer++;
}

/**********************************************************************
 * Function - SwapRows
 * Swap two rows of the current rectangle
 */

void SwapRows(char** rect, int s1, int s2, int r, int c)
{
  int x; 
  char temp; 

  for(x=0; x<c; x++) {
    temp = rect[s1-1][x];
    rect[s1-1][x] = rect[s2-1][x]; 
    rect[s2-1][x] = temp;
  }
  swaps[swap_pointer].val1 = s1;
  swaps[swap_pointer].val2 = s2;
  swaps[swap_pointer].type = 'r';
  swap_pointer++;
}

/*******************************************************************
 * PrintVowelCount
 * Print the # of vowels in the ciphertext
 */

void PrintVowelCount()
{
   int row_count = 1, danswer, vowel_count = 0, x;                             

   printf("\n   Enter a valid rectangle width --> ");
   scanf("%d",&danswer);
   if ( ( message_length % danswer ) != 0 ) {
      printf("   Invalid rectangle width %d.\n",danswer);
      printf("   Returning to main menu...\n\n");
   }
   printf("\n");
   for ( x = 0; x < message_length; x++ ) {
      if ( x > 0 && ( x % danswer ) == 0 ) {
         printf("   Row number %d - %d vowels.\n",row_count,
                vowel_count);
         row_count++;
         vowel_count = 0;
      }
      if ( message[x] == 'a' || message[x] == 'e' ||
           message[x] == 'i' || message[x] == 'o' ||
           message[x] == 'u' ||
           message[x] == 'A' || message[x] == 'E' ||
           message[x] == 'I' || message[x] == 'O' ||
           message[x] == 'U' ) vowel_count++;
   }
   printf("   Row number %d - %d vowels.\n",row_count, vowel_count);
   row_count = 1;
   vowel_count == 0;
   printf("\n\n");
}

/*******************************************************************
 * Function - PrintRect
 * Print the current working rectangle on the screen
 */

void PrintRect(char** rect, int rows, int columns)
{
 int i, j, x;

   printf("\n   Current Rectangle on a width of %d\n\n",columns);
   if (columns > 9) {
      printf("         ");
      for (x=10; x<=columns; x++) printf("%i",(x % 100)/10); 
      printf("\n");
   } 
   for (x=1; x<=columns; x++) printf("%d",x%10);
   printf("\n");
   for (x=1; x<=columns; x++) printf("-");
   printf("\n");

   for(i=0; i<rows; i++) {
     for(j=0; j<columns; j++) {
       printf("%c", rect[i][j]);
     }
     printf("  |%3d", i+1);
     printf("\n");
   }
   printf("\n");

}

/*******************************************************************
 * Function - PrintRectangleSizes
 * Print the valid rectangle sizes for the ciphertext
 * width*height must equal ciphertext length
 */

void PrintRectangleSizes()
{
   int size_count = 0,                  /* Count of valid sizes.        */
       x;                               /* Simple counter.              */

   printf("\n   Possible rectangle sizes are...\n");
   printf("     ");
   for ( x = 1; x <= message_length; x++ ) {
      if ( ( message_length % x ) == 0 ) {
         size_count++;
         printf("%d X %d   ",x,message_length / x);
         if ( size_count == 5 ) {
            printf("\n     ");
            size_count = 0;
         }
      }
   }
   printf("\n\n");
}




