/********************************************************************
 * smc.c -- Simple Matrix Cipher                                    *
 * Author: stderr (stderr.dev@gmail.com)                            *
 *                                                                  *
 * Purpose: Demonstrate simple encryption by using matrices.        *
 * Multiplying text (split into 1x3 matrices) by the 3x3 encryption *
 * matrix. To decipher with this method, just multiply the          *
 * encrypted numbers by the inverse of the 3x3 matrix.              *
 *                                                                  *
 * This program was written to run on linux, but it should run fine *
 * using cygwin.                                                    *
 *                                                                  *
 * Note: Change the matrices in encrypt() and decrypt() you please, *
 * Just make sure the decrypt() matrix is the inverse of the matrix *
 * used in encrypt(). I use my scientific calculator to figure out  *
 * the inverse of a matrix.                                         *
 ********************************************************************/

#include <stdio.h>
#include <string.h>

/* Function prototypes */
void encrypt(int matrix[3]);
void decrypt(int matrix[3]);
void menu(void);

/* Global variable declaration. */
int new[3] = { 0, 0, 0}; /* new contains the newly encrypted or decrypted data. */

int main(void)
{
  int test[3] = { 0,0,0 };
  int x,y=-1;
  char mesg[5],data[1024],name[256],mchar,*my_ptr;
  FILE *my_file;

  printf("Welcome to Simple Matrix Cipher (SMC).\n");
  while(1) {
    menu();
    fgets(mesg,sizeof(mesg),stdin);
    switch(mesg[0]) {
    case '1': /* Encryption was chosen. */
      printf("Output Filename: ");
      fgets(name,sizeof(name),stdin);
      name[strlen(name)-1] = '\0'; /* Chop off the \n passed by fgets() */
      my_file = fopen(name,"w");
      if (my_file == NULL) {
	printf("Error opening file.\n");
	break;
      }
      printf("Enter line to encrypt: ");
      fgets(data,sizeof(data),stdin);
      y = -1; /* y is used to keep track of when we should encrypt the next 1x3 matrix. */
      /* Once the loop has gone through 3 iterations the program encrypts that data. */
      for (x = 0; x < strlen(data); ++x) {
	y++;
	test[y] = data[x];
	if (y == 2) {
	  encrypt(test);
	  /* Print the encrypted data to the output file given, as well as to stdout. */
	  fprintf(my_file,"%d %d %d ",new[0],new[1],new[2]);
	  printf("%d %d %d ",new[0],new[1],new[2]);
	  test[0] = test[1] = test[2] = 0; /* Reset test[] */
	  y = -1;
	}
      }
      /* Check to see if there is still data that needs to be encrypted.
       * The only way this will happen is if the number of characters isn't divisible by 3.
       * This test (test[0] != 0) is why resetting the test variable is essential. */
      if (test[0] != 0) {
	encrypt(test);
	fprintf(my_file,"%d %d %d",new[0],new[1],new[2]);
	printf("%d %d %d\n",new[0],new[2],new[3]);
      }
      fprintf(my_file,"\n");
      fclose(my_file);
      break;
    case '2': /* Decryption was chosen. */
      printf("Input Filename: "); /* The file given should contain encrypted data. */
      fgets(name,sizeof(name),stdin);
      name[strlen(name)-1] = '\0'; /* Remove the \n passed by fgets() */
      my_file = fopen(name,"r");
      if (my_file == NULL) {
	printf("File not found!\n");
	break;
      }

      x = -1;
      y = 0; /* y is now used as the index of test[] */
      /* WTF??? why didn't I use meaningful names ... oh well... */
      while(1) {
	if ((mchar=fgetc(my_file)) != ' ' && mchar != EOF) {
	  data[++x] = mchar;
	}
	if (mchar == ' ') {
	  data[++x] = '\0'; /* Isolate each encrypted number as a string of it's own. */
	  x=-1;
	  test[y] = atoi(data);
	  if (y == 2) {
            y = 0;
            decrypt(test);
	    /* Print the decrypted text to the screen. */
            printf("%c%c%c",new[0],new[1],new[2]);
            test[0] = test[1] = test[2] = 0;
            continue;
          }
	  y++;
	}
	if (mchar == EOF)
	  break;
      }
      /* Decrypt the remaining text. */
      decrypt(test);
      printf("%c%c%c",new[0],new[1],new[2]);
      test[0] = test[1] = test[2] = 0;
      fclose(my_file);
      break;
    case '3': /* About was chosen. */
      printf("       Author: stderr        \n");
      printf("--- Simple Matrix Ciphers ---\n");
      printf("         Version 1.1         \n");
      break;
    case '4': /* Exit was chosen */
      printf("Thanks for using SMC!\n");
      exit(1);
    default: /* User is an idiot. */
      printf("Command: %s not found.\n",mesg);
      break;
    }
  }
}

/* Encryption algorithm. */
void encrypt(int matrix[3])
{
  int eshift[3][3] = {
    { 1, -2, 2 },
    { -1, 1, 3 },
    { 1, -1, -4 }};

  new[0] = matrix[0] * eshift[0][0] + matrix[1] * \
    eshift[1][0] + matrix[2] * eshift[2][0];
  new[1] = matrix[0] * eshift[0][1] + matrix[1] * \
    eshift[1][1] + matrix[2] * eshift[2][1];
  new[2] = matrix[0] * eshift[0][2] + matrix[1] * \
    eshift[1][2] + matrix[2] * eshift[2][2];
}

/* Decryption algorithm.*/
void decrypt(int matrix[3])
{
  int dshift[3][3] = { /* This matrix is the inverse of eshift[3][3] in function encrypt() */
    { -1, -10, -8 },
    { -1, -6, -5 },
    { 0, -1, -1 }};

  new[0] = matrix[0] * dshift[0][0] + matrix[1] * \
    dshift[1][0] + matrix[2] * dshift[2][0];
  new[1] = matrix[0] * dshift[0][1] + matrix[1] * \
    dshift[1][1] + matrix[2] * dshift[2][1];
  new[2] = matrix[0] * dshift[0][2] + matrix[1] * \
    dshift[1][2] + matrix[2] * dshift[2][2];
}

/* Program menu */
void menu(void)
{
  printf("\n1 - Encrypt Data\n");
  printf("2 - Decrypt Data\n");
  printf("3 - About\n");
  printf("4 - Exit\n");
  printf(": ");
} 
