/************************************************************************/
/*                                                                      */
/*                                                                      */
/*Columnar Transposition Cipher Decryption Tool                         */
/*                                                                      */
/*                                                                      */
/************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MSGLEN 670
#define TRUE     1
#define FALSE    0

int  ReadMessage();
void ExploreRectangleSizes();
void VowelCountRows(char[], int, int);
void ExploreTransposedRectangleSizes();
void Transpose(int, int);
void FindTrigrams(char[], int, int);

FILE *fp1;                              /* Message file pointer.        */
FILE *fp2;                              /* Output file pointer.         */

int message_length;                     /* Length of cipher.            */
 
char temp[MSGLEN];                      /* Scrap message array.         */
char message[MSGLEN];                   /* Starting message array.      */

int main(int argc, char* argv[]) {
fpos_t pos;

    /* Check the number of program arguments. */
    if ( argc != 3 && argc != 2 ) {
	    fprintf(stderr, "Usage: %s <message_file> [output_file]\n", argv[0]);
	    fprintf(stderr, "  output_file is optional.  If not specified,\n");
	    fprintf(stderr, "  output is sent to stdout.  If specified, output\n");
	    fprintf(stderr, "  is sent to output_file\n");
	    fprintf(stderr, "Note:  please remove white space (but not new lines\n");
	    fprintf(stderr, "  from the message_file (cipher)) before using\n");
	    exit(-1);
    }
  
    /* Open the message file. */
    if ((fp1 = fopen(argv[1],"r")) == NULL ) {
	    fprintf(stderr, "Message file %s does not exist.\n", argv[1]);
	    exit(-1);
    }

    /* Test MSGLEN size 
    fseek(fp1, 0L, SEEK_END);
    fgetpos(fp1, &pos);
    if(MSGLEN < pos) {
	    fprintf(stderr, "Message file is too long.\n");
        fprintf(stderr, "Increase value of MSGLEN and recompile.\n");
        fprintf(stderr, "MSGLEN is %d.\n", MSGLEN);
        fprintf(stderr, "File size is %ld.\n", (unsigned long)pos);
        exit(2);
	}  */
    fseek(fp1, 0L, SEEK_SET);    
  
    if(3 == argc) {
        /* Open the output file. */
        fp2 = fopen(argv[2],"w");
        if(NULL == fp2) {
            fprintf(stderr, "Unable to open output file %s\n", argv[2]);
            fclose(fp1);
            exit(-1);
        }
    } else {
        fp2 = stdout;
    }

    fprintf(fp2, "Reading Cipher\n");
    message_length = ReadMessage();

    fprintf(fp2, "\nExploring Cipher\n");
    ExploreRectangleSizes();

    fprintf(fp2, "\nRotating Ciper Clockwise\n");
    ExploreTransposedRectangleSizes();

    fclose(fp1);
    fclose(fp2);

    return(0);
      
}

void ExploreRectangleSizes() {
int x = 0;						 
  
    for ( x = 1; x <= message_length; x++ ) {
	    if ( ( message_length % x ) == 0 ) {
            if(1 == x || 2 == x || 3 == x ||
               message_length == x || message_length/2 == x || message_length/3 == x) {
                continue;
            }

	        fprintf(fp2, "\nExploring Vowel Count %d X %d\n", x, message_length / x);
            VowelCountRows(message, x, message_length / x);
	        fprintf(fp2, "\nExploring Trigrams %d X %d\n", x, message_length / x);
            FindTrigrams(message, x, message_length / x);
	    }
    }
    printf("\n");
}

int ReadMessage() {
int pos=0,nch=0;
  
    memset(message, 0, MSGLEN * sizeof(char));

    while(((nch=getc(fp1))!=EOF)&&pos<MSGLEN) {
	    if(nch>=32&&nch<128) {
	        message[pos++]=(char)nch;
        }
    }
    message[pos]='\0';
    return(pos);
}

void VowelCountRows(char message[], int rows, int columns) {
int  pos   = 0;
int  count = 0;
int  r = 0;
int  c = 0;
char ch = 0;

    for(r=0; r<rows; r++) {
        count = 0;
        for(c=0; c<columns; c++) {
            pos = r * columns + c;
            ch = message[pos];
	        if ( ch == 'a' || ch == 'e' ||
	             ch == 'i' || ch == 'o' ||
	             ch == 'u' ||
	             ch == 'A' || ch == 'E' ||
	             ch == 'I' || ch == 'O' ||
	             ch == 'U' ) {
                count++;
            }
            
            fprintf(fp2, "%c ", ch);           
        }

        fprintf(fp2, "  Row %2d: %2d\n", r+1, count);
    }
}

void Transpose(int rows, int columns) {
int  cr = 0;    /* current row in message[]      */
int  cc = 0;    /* current column in message[]   */
int  nr = 0;    /* new row in temp[]             */
int  nc = 0;    /* new column in temp[]          */
int  cl = 0;    /* current linear into message[] */
int  nl = 0;    /* new linear into temp[]        */

char c = 0;

    /* zero temporary array */
    memcpy(temp, message, MSGLEN * sizeof(char));

    for(cr=0; cr<rows; cr++) {
        for(cc=0; cc<columns; cc++) {

            /* current linear */
            cl = cr * columns + cc;
            /* current character */
            c = message[cl];

            /* logically swaping a row for a column */
            nr = cc;
            /* logically swaping a column for a row */
            nc = rows - cr;
            /* calculate new linear */
            nl = nr * rows + nc - 1;
            /* write it */
            temp[nl] = c;
        }
    }
}

void ExploreTransposedRectangleSizes() {
int x = 0;						 
  
    for ( x = 1; x <= message_length; x++ ) {
	    if ( ( message_length % x ) == 0 ) {
            /* discard trivial lengths */
            if(1 == x || 2 == x || 3 == x ||
               message_length == x || message_length/2 == x || message_length/3 == x) {
                continue;
            }

            Transpose(x, message_length / x);
	        fprintf(fp2, "\nExploring Transposed Vowel Count %d X %d\n", message_length / x, x);
            VowelCountRows(temp, message_length / x, x);
	        fprintf(fp2, "\nExploring Transposed Trigrams %d X %d\n", message_length / x, x);
            FindTrigrams(temp, message_length / x, x);
	    }
    }
    printf("\n");
}

void FindTrigrams(char message[], int rows, int columns) {
int T = FALSE;
int H = FALSE;
int E = FALSE;
int A = FALSE;
int N = FALSE;
int D = FALSE;
int  pos = 0;
int  r = 0;
int  c = 0;
char ch = 0;

    for(r=0; r<rows; r++) {
        T = FALSE; H = FALSE; E = FALSE;
        A = FALSE; N = FALSE; D = FALSE;
        for(c=0; c<columns; c++) {

            pos = r * columns + c;
            ch = message[pos];

	        if ( ch == 'T' || ch == 't') {
                T = TRUE;
            }
            
	        if ( ch == 'H' || ch == 'h') {
                H = TRUE;
            }
	        if ( ch == 'E' || ch == 'e') {
                E = TRUE;
            }

	        if ( ch == 'A' || ch == 'a') {
                A = TRUE;
            }
            
	        if ( ch == 'N' || ch == 'n') {
                N = TRUE;
            }
	        if ( ch == 'D' || ch == 'd') {
                D = TRUE;
            }

            fprintf(fp2, "%c ", ch);           
        }

        fprintf(fp2, "  ");

        if(TRUE == T && TRUE == H && TRUE == E) {
            fprintf(fp2, "THE ");
        }

        if(TRUE == A && TRUE == N && TRUE == D) {
            fprintf(fp2, "AND ");
        }

        fprintf(fp2, "\n");

    }
}



