/*************************************************************************/
/*PRIYA RAMAKRISHNAN                                                     */
/*                                                                       */
/*Columnar Transposition Cipher Decryption Tool                          */
/*CMSC 443: DR. STEPHENS & DR. PINKSTON                                  */
/*-----------------------------------------------------------------------*/
/* To compile: cc col.c -o col                                           */
/* To execute: col inputFileName outputFileName                          */
/*-----------------------------------------------------------------------*/
/* This program is an tool that can be used to decrypt plaintext that    */  
/* has been encrypted with a columnar transposition cipher system.       */  
/*-----------------------------------------------------------------------*/
/* Made the following improvements to this tool. Added options to:       */
/* 1) read data into a specified rectangle by columns                    */   
/* 2) permute rows of data                                               */
/* 3) read data out of a rectangle to a file by columns                  */
/* 4) provided vowel count by columns                                    */
/* 5) made other modifactions in "style": removed several global vars,   */
/*    and provided function declarations.                                */
/*************************************************************************/


#include<stdio.h>
#include<string.h>
#include <stdlib.h>

#define MSGLEN 5000

int Read_Message();
void Print_Main_Menu();
void Print_Sub_Menu(char);
void Print_To_File_Rows(int);
void Print_To_File_Columns(int);
void Print_Cipher_by_Rows(char[],int);
void Print_Cipher_by_Column(char[],int);
void Solving_Mode_Rows();
void Solving_Mode_Columns();
void Swap(int,int,int,int);
void Print_Vowel_Count(char);
void Print_Rectangle_Sizes();



FILE *fp1,                              /* Message file pointer.        */
  *fp2;                                 /* Output file pointer.         */

int message_length;                     /* Length of cipher.            */
 
char current_message[MSGLEN],           /* Current message array.       */
  message[MSGLEN];                      /* Starting message array.      */

int main(int argc, char* argv[])
{
  int x;                                /* Simple counter.              */
  int danswer,input;
  
  /* Check the number of program arguments.                             */
  
  if ( argc != 3 ) {
    printf("Usage: sts message_file output_file\n");
    exit(0);
  }
  
  /* Open the message file.                                             */
  
  if ((fp1 = fopen(argv[1],"r")) == NULL ) {
    printf("Message file %s does not exist. \n",argv[1]);
    exit(2);
  }
  
  /* Open the output file.                                              */ 
  
  fp2 = fopen(argv[2],"w");
  
  /* Read in the message.                                               */
  
  message_length = Read_Message();
  
  /* Print the initial main menu to the screen.                         */
  
  printf("\nWelcome to the Simple Transposition Solver.\n\n");
  Print_Main_Menu();
  printf("Enter a command --> ");
  
  /* Interactively process user requests.                               */
  
  while ( scanf("%d",&danswer) > 0 ) {
    
    /*If option 1, print out the cipher text by rows.                   */
    
    if ( danswer == 1 ) {
      printf("\n   Enter a valid rectangle width --> ");
      scanf("%d",&input);
      if ( ( message_length % input ) != 0 ) {
	printf("   Using default width of 50.\n");
	input = 50;
      }
      Print_Cipher_by_Rows(message,input);
    }

    /*If option 2, print out the cipher text by columns.                */
    
    if(danswer == 2){
      printf("\n Enter a valid rectangle height --> ");
      scanf("%d",&input);
      if( (message_length%input ) != 0) {
	printf("   Using default height of 50.\n");
	input = 50;
      }
      Print_Cipher_by_Column(message,input);
    }	
      
    /*If option 3, print out the character count of the cipher.         */

    if ( danswer == 3 ) {
      printf("\n   There are %i characters in the message.\n\n",
	     message_length);
      }
    
    /*If option 4, print out possible rectangle sizes.                  */
    
    if ( danswer == 4 ) {
      Print_Rectangle_Sizes();
    }
    
    /*If option 5, print out vowel counts by row.                       */
    
    if ( danswer == 5 ) {
      Print_Vowel_Count('R');
    }
    
    /*If option 6, print vowel counts by column.                        */
    
    if (danswer == 6) {
      Print_Vowel_Count('C');
    }			
    
    /*If option 7, enter solving mode for rows.                         */
    
    if ( danswer == 7 ) {
      printf("\n   Entering solving mode...\n");
      Solving_Mode_Rows();
    }
    
    /*If option 8, enter solving mode for columns                       */
    
    if ( danswer == 8 ) {
      printf("\n   Entering solving mode...\n");
      Solving_Mode_Columns();
    } 

    /*If option 9, exit the program.                                    */
    
    if ( danswer == 9 ) {
      printf("Program exiting...\n\n");
      exit(0);
      }
    Print_Main_Menu();
    printf("Enter a command --> ");
  }
  return 0;
}



/*******************************************************************/
/*                                                                 */
/*                     FUNCTION READ_MESSAGE                       */
/*                                                                 */
/*     Function READ_MESSAGE reads in the cipher from the input    */
/*     file.                                                       */
/*                                                                 */
/*******************************************************************/

int Read_Message()
{
  int pos=0,nch;
  
  while(((nch=getc(fp1))!=EOF)&&pos<MSGLEN)
    if(nch>=32&&nch<128)
      message[pos++]=nch;
  
  message[pos]='\0';
  return(pos);
}



/*******************************************************************/
/*                                                                 */
/*                     FUNCTION PRINT_MAIN_MENU                    */
/*                                                                 */
/*     Function PRINT_MAIN_MENU prints the main menu options to    */
/*     the screen.                                                 */
/*                                                                 */
/*******************************************************************/

void Print_Main_Menu()
{
  printf("             Main Menu Options\n");
  printf("             -----------------\n");
  printf("     1) Display the cipher text by rows.\n");
  printf("     2) Display the cipher text by columns.\n");
  printf("     3) Count the total number of characters.\n");
  printf("     4) Print possible rectangle sizes.\n");
  printf("     5) Print vowel count by row.\n");
  printf("     6) Print vowel count by column.\n");	
  printf("     7) Enter solving mode-Rows.\n");
  printf("     8) Enter solving mode-Columns.\n");
  printf("     9) Quit.\n\n");
}



/*******************************************************************/
/*                                                                 */
/*                     FUNCTION PRINT_SUB_MENU                     */
/*                                                                 */
/*     Function PRINT_SUB_MENU print the solving mode menu to      */
/*     the screen.                                                 */
/*                                                                 */
/*******************************************************************/

void Print_Sub_Menu(char mode)
{
  printf("             Solving Mode Options\n");
  printf("             --------------------\n");
  if(mode == 'R')
    printf("     1) Permute two columns of text.\n");
  else
    printf("     1) Permute two rows of text.\n");
  printf("     2) Undo the last permutation.\n");
  printf("     3) Undo all permutations (Start over).\n");
  printf("     4) Print the current rectangle to output file.\n");
  printf("     5) Return to main menu.\n\n");
}



/*******************************************************************/
/*                                                                 */
/*                 FUNCTION PRINT_TO_FILE_ROWS                     */
/*                                                                 */
/*     Function PRINT_TO_FILE prints the current rectangle to      */
/*     the output file by rows.                                    */
/*                                                                 */
/*******************************************************************/

void Print_To_File_Rows(int width)
{
   int x;               
   
   fprintf(fp2,"\n   ");
   for ( x = 0; x < message_length; x++ ) {
     if ( x > 0 && ( x % width ) == 0 ) {
       fprintf(fp2,"\n   ");
     }
     fprintf(fp2,"%c",current_message[x]);
   }
   fprintf(fp2,"\n");
}

/*******************************************************************/
/*                                                                 */
/*                 FUNCTION PRINT_TO_FILE_COLUMNS                  */
/*                                                                 */
/*     Function PRINT_TO_FILE_COLUMNS prints the current rectangle */ 
/*     to the output file by columns.                              */
/*                                                                 */
/*******************************************************************/

void Print_To_File_Columns(int height)
{
  int i,j,cnt;          
  int width;
  char temp[MSGLEN][MSGLEN];

  if(message_length%height == 0)
    width=message_length/height;
  else width = message_length/height + 1;
  
  /*Store in temp array by height and print out by width*/
  cnt=0;
  for( i = 0; i<width; i++)
    {
      for(j=0; j<height; j++)
	{
	  if(cnt < message_length){
	    temp[i][j]=current_message[cnt];
	    cnt++;
	  }
	  else break;
	}
      if(cnt>message_length) break;
    }
 
  fprintf(fp2,"\n   ");
  for(i=0; i<height; i++)
    { 
      fprintf(fp2,"\n   ");
      for(j=0; j<width; j++)
	{
	  fprintf(fp2,"%c",temp[j][i]);
	}
     
    }
  printf("\n\n");
  
}


/*******************************************************************/
/*                                                                 */
/*                  FUNCTION PRINT_CIPHER_BY_ROWS                  */
/*                                                                 */
/* Function PRINT_CIPHER_BY_ROWS prints the cipher text to the     */ 
/*screen by Rows.                                                  */ 
/*                                                                 */
/*******************************************************************/

void Print_Cipher_by_Rows(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 PRINT_CIPHER_BY_COLUMN                */
/*                                                                 */
/* Function PRINT_CIPHER_BY_COLUMN prints the cipher text to the   */
/* screen by Columns.                                              */ 
/*                                                                 */
/*******************************************************************/

void Print_Cipher_by_Column(char msg[],int height)
{
  int i,j,cnt;          
  int width;
  char temp[MSGLEN][MSGLEN];
  
  printf("\n   Cipher text on a height of %d\n\n   ",height);
  printf("\n");
  
  if(message_length%height == 0)
    width=message_length/height;
  else width = message_length/height + 1;

  /*Store in temp array by height and print out by width*/
  cnt=0;
   for( i = 0; i<width; i++)
    {
      for(j=0; j<height; j++)
	{
	  if(cnt < message_length){
	    temp[i][j]=msg[cnt];
	    cnt++;
	  }
	  else break;
	}
      if(cnt>message_length) break;
    }

  cnt=1;
  for(i=0; i<height; i++)
    {
      printf("\n%2d|", cnt);
      for(j=0; j<width; j++)
	{
	  printf("%c", temp[j][i]);
	}
     
      cnt++;
    }
  printf("\n\n");
} 

/*******************************************************************/
/*                                                                 */
/*                 FUNCTION SOLVING_MODE_ROWS                      */
/*                                                                 */
/*     Function SOLVING_MODE_ROWS puts the user into message       */
/*     solving mode - Ciphertext Displayed by Rows.                */
/*                                                                 */
/*******************************************************************/

void Solving_Mode_Rows()
{
  int columns = 0,                   /* Number of columns in rect.   */
    rows = 0,                        /* Number of rows in rectangle. */
    swap1,                           /* Column to swap.              */
    swap2,                           /* Column to swap.              */
    x;                               /* Simple counter.              */
  int danswer;
  
  int swaps[2][1000],                /* List of user permutes.       */
    swap_pointer = 0;                /* Pointer to current swap.     */

  
  printf("\n   Enter a valid rectangle width -- > ");
  scanf("%d",&danswer);
  while ( ( message_length % danswer ) != 0 ) {
    printf("   Invalid rectangle width %d.\n",danswer);
    printf("\n   Enter a valid rectangle width --> ");
    scanf("%d",&danswer);
  }
  printf("\n");
  columns = danswer;
  rows = message_length / columns;
  
  /*     Copy the original cipher text into the current cipher 
	 text array.                                                 */
  
  for ( x = 0; x < MSGLEN; x++ ) {
    current_message[x] = message[x];
  }
  
  /*     Print out the current rectangle and then the sub-menu.      */
  
  Print_Cipher_by_Rows(current_message,columns);
  Print_Sub_Menu('R');
  printf("Enter a command --> ");

  /*     Interactively process user requests.                        */
  
  while ( scanf("%d",&danswer) > 0 ) {
    
    /*     If option 1, permute two columns of the rectangle.        */

    if ( danswer == 1 ) {
      printf("\n   Enter one column number --> ");
      scanf("%d",&swap1);
      printf("   Enter the second column number --> ");
      scanf("%d",&swap2);
      while ( swap1 < 1 || swap1 > columns ||
	      swap2 < 1 || swap2 > columns ) {
	printf("   Invalid column number given.\n");
	printf("\n   Enter one column number --> ");
	scanf("%d",&swap1);
	printf("   Enter the second column number --> ");
	scanf("%d",&swap2);
      }
      swaps[0][swap_pointer] = swap1;
      swaps[1][swap_pointer] = swap2;
      swap_pointer++;
      Swap(swap1,swap2,rows,columns);
    }
    
     /*     If option 2, undo the last permutation.                  */
    
      if ( danswer == 2 ) {
	if ( swap_pointer > 0 ) {
	  printf("\n   Undoing the previous permutation...\n\n");
	  swap_pointer--;
	  swap1 = swaps[0][swap_pointer];
	  swap2 = swaps[1][swap_pointer];
	  Swap(swap1,swap2,rows,columns);
         }
	else {
	  printf("\n   No previous permutations to undo...\n\n");
	}
      }
      
     /*     If option 3, erase all previous permutations.            */
      
      if ( danswer == 3 ) {
	printf("\n   Erasing all previous permutations...\n\n");
	for ( x = 0; x < MSGLEN; x++ ) {
            current_message[x] = message[x];
	}
	swap_pointer = 0;
      }
      
     /*     If option 4, print the current rectangle to the output
            file.                                                      */
      
      if ( danswer == 4 ) {
	printf("\n   Printing current rectangle to output file...\n\n");
	Print_To_File_Rows(columns);
      }
      
      /*     If option 5, return to the main menu.                      */
      
      if ( danswer == 5 ) {
	printf("\n   Returning to main menu...\n\n");
	return;
      }
      Print_Cipher_by_Rows(current_message,columns);
      Print_Sub_Menu('R');
      printf("\nEnter a command --> ");
  }
} 


/*******************************************************************/
/*                                                                 */
/*                   FUNCTION SOLVING_MODE_COLUMNS                 */
/*                                                                 */
/*     Function SOLVING_MODE_COLUMNS puts the user into message    */
/*      solving mode -Ciphertext displayed as Columns              */    
/*                                                                 */
/*******************************************************************/

void Solving_Mode_Columns()
{
  int columns = 0,                   /* Number of columns in rect.   */
    rows = 0,                        /* Number of rows in rectangle. */
    swap1,                           /* Column to swap.              */
    swap2,                           /* Column to swap.              */
    x;                               /* Simple counter.              */
  int danswer;

  int swaps[2][1000],                /* List of user permutes.       */
    swap_pointer = 0;                /* Pointer to current swap.     */
  
  printf("\n   Enter a valid rectangle height -- > ");
  scanf("%d",&danswer);
  while ( ( message_length % danswer ) != 0 ) {
    printf("   Invalid rectangle height %d.\n",danswer);
    printf("\n   Enter a valid rectangle height --> ");
    scanf("%d",&danswer);
  }
  printf("\n");
  rows = danswer;
  columns = message_length / rows;
  
  /*     Copy the original cipher text into the current cipher 
	 text array.                                                 */
  
  for ( x = 0; x < MSGLEN; x++ ) {
    current_message[x] = message[x];
  }
  
  /*     Print out the current rectangle and then the sub-menu.      */
  
  Print_Cipher_by_Column(current_message,rows);
  Print_Sub_Menu('C');
  printf("Enter a command --> ");

  /*     Interactively process user requests.                        */
  
  while ( scanf("%d",&danswer) > 0 ) {
    
    /*     If option 1, permute two rows of the rectangle.           */

    if ( danswer == 1 ) {
      printf("\n   Enter one row number --> ");
      scanf("%d",&swap1);
      printf("   Enter the second row number --> ");
      scanf("%d",&swap2);
      while ( swap1 < 1 || swap1 > rows ||
	      swap2 < 1 || swap2 > rows ) {
	printf("   Invalid row number given.\n");
	printf("\n   Enter one row number --> ");
	scanf("%d",&swap1);
	printf("   Enter the second row number --> ");
	scanf("%d",&swap2);
      }
      swaps[0][swap_pointer] = swap1;
      swaps[1][swap_pointer] = swap2;
      swap_pointer++;
      Swap(swap1,swap2,columns,rows);
    }
    
     /*     If option 2, undo the last permutation.                  */
    
      if ( danswer == 2 ) {
	if ( swap_pointer > 0 ) {
	  printf("\n   Undoing the previous permutation...\n\n");
	  swap_pointer--;
	  swap1 = swaps[0][swap_pointer];
	  swap2 = swaps[1][swap_pointer];
	  Swap(swap1,swap2,columns,rows);
         }
	else {
	  printf("\n   No previous permutations to undo...\n\n");
	}
      }
      
     /*     If option 3, erase all previous permutations.               */
      
      if ( danswer == 3 ) {
	printf("\n   Erasing all previous permutations...\n\n");
	for ( x = 0; x < MSGLEN; x++ ) {
            current_message[x] = message[x];
	}
	swap_pointer = 0;
      }
      
     /*     If option 4, print the current rectangle to the output
            file.                                                       */
      
      if ( danswer == 4 ) {
	printf("\n Printing current rectangle to output file...\n\n");
	Print_To_File_Columns(rows);
      }
      
      /*     If option 5, return to the main menu.                       */
      
      if ( danswer == 5 ) {
	printf("\n   Returning to main menu...\n\n");
	return;
      }
      Print_Cipher_by_Column(current_message,rows);
      Print_Sub_Menu('C');
      printf("\nEnter a command --> ");
  }
} 



/*******************************************************************/
/*                                                                 */
/*                     FUNCTION SWAP                               */
/*                                                                 */
/*     Function SWAP swaps two columns or rows of cipher.          */
/*                                                                 */
/*******************************************************************/

void Swap(int s1,int s2,int r,int c)
{
  int x;
  char hold;
   
   for ( x = 0; x < r; x++ ) {
     hold = current_message[((x*c) + s1) - 1];
     current_message[((x*c)+s1)-1] = current_message[((x*c)+s2)-1];
     current_message[((x*c)+s2)-1] = hold;
   }
}



/*******************************************************************/
/*                                                                 */
/*                   FUNCTION PRINT_VOWEL_COUNT                    */
/*                                                                 */
/*     Function PRINT_VOWEL_COUNT prints the vowel count by rows   */
/*               or columns.                                       */
/*******************************************************************/

void Print_Vowel_Count(char mode)
{
  int row_col_count = 1,             /* Counts current message row/col.*/
    vowel_count = 0,                 /* Total number of vowels.        */
    x;                               /* Simple counter.                */
  int danswer;
  
  if(mode == 'R')
    printf("\n   Enter a valid rectangle width --> ");
  else printf("\n   Enter a valid rectangle height --> "); 	
  
  scanf("%d",&danswer);
  if ( ( message_length % danswer ) != 0 ) {
    if(mode == 'R')
      printf("   Invalid rectangle width %d.\n",danswer);
    else printf("   Invalid rectangle height %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 ) {
      if(mode == 'R')
	printf("   Row number %d - %d vowels.\n",row_col_count,
	       vowel_count);
      else printf("   Column number %d - %d vowels.\n",row_col_count,
		  vowel_count);
      row_col_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++;
  }
  if(mode == 'R')
    printf("   Row number %d - %d vowels.\n",row_col_count, vowel_count);
  else printf("   Column number %d - %d vowels.\n",row_col_count, vowel_count);
  row_col_count = 1;
  vowel_count == 0;
  danswer = 0;
  printf("\n\n");
}



/*******************************************************************/
/*                                                                 */
/*                   FUNCTION PRINT_RECTANGLE_SIZES                */
/*                                                                 */
/*     Function PRINT_RECTANGLE_SIZES prints out possible          */
/*     rectangle sizes.                                            */
/*                                                                 */
/*******************************************************************/

void Print_Rectangle_Sizes()
{
  int size_count = 0,                  /* Count of valid sizes.        */
    x;                       
  
  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");
}




