/**************************************************/
/**** Author:       Joel Castellanos           ****/
/**** Course:       CS-241                     ****/
/**** Updated:      02/25/2009                 ****/  
/**************************************************/
#include <stdio.h>
#include <string.h>
#define DEBUG 0
#define MAP_LENGTH 7
#define KEY_LENGTH 6
#define MAX_STRING_LENGTH 1025

int const ACTION_ENCRYPT = 1;
int const ACTION_DECRYPT = 2;
int const ACTION_ENCRYPT_STRONG = 3;
int const ACTION_DECRYPT_STRONG = 4;
int action = 0;

char* key;
unsigned char* inStr;
unsigned char outStr[MAX_STRING_LENGTH];

char keySource[MAP_LENGTH];
char keySink[MAP_LENGTH];

char map[MAP_LENGTH];

//==================================================================================
// readParameters
//
// Check command line arguments for correct syntax
// Return 0 if no errors found.
//==================================================================================
int readParameters(int argc, char *argv[])
{ if (argc != 4)
  { printf("Error** cipher expects exactly four arguments.\n");
    return -1;
  }
  key   = argv[2];
  inStr = argv[3];

  char* actionCode = argv[1];
  if (strcmp(actionCode,"-e")==0) action = ACTION_ENCRYPT;
  else if (strcmp(actionCode,"-d")==0) action = ACTION_DECRYPT;
  else if (strcmp(actionCode,"-E")==0) action = ACTION_ENCRYPT_STRONG;
  else if (strcmp(actionCode,"-D")==0) action = ACTION_DECRYPT_STRONG;
  else
  { printf("Error** first argument must be -e, -d, -E, -D.\n");
    return -1;
  }
  return 0;
}

//==================================================================================
// parseKey
//
// Parse the key into src and sink parts.
// Return 0 if no errors found.
//==================================================================================
int parseKey()
{ int i;
  for (i=0; i<KEY_LENGTH*2; i++)
  { char c = key[i];
    if (c < '0' || c > '7')
    { printf("Error** key must consist of 12 octal digits.\n");
      return -1;
    }
    char digit = c-'0';
    int idx = i/2;
    if (i % 2) keySink[idx] = digit;
    else keySource[idx] = digit;
  }

  //Add default 7th key: 00
  keySource[MAP_LENGTH-1] = 0;
  keySink[MAP_LENGTH-1] = 0;

  if (DEBUG)
  { printf ("Key Source: ");
    for (i=0; i<MAP_LENGTH; i++) printf("%d ", keySource[i]);
    printf("\n");

    printf ("key Sink:   ");
    for (i=0; i<MAP_LENGTH; i++) printf("%d ", keySink[i]);
    printf("\n\n");
  }
 
  return 0;
}

//==================================================================================
// buildMap 
//
// sets the values in global arrays: map.
// The map array will either be an encryption map or a decryption map depending
//   on the input argument.
// Return 0 if no errors found.
//==================================================================================
void buildMap()
{ int i=0;
  int mapEncrypt[MAP_LENGTH];
  int mapDecrypt[MAP_LENGTH];

  //initililize maps
  for (i=0; i<MAP_LENGTH; i++) 
  { mapEncrypt[i] = -1;
    mapDecrypt[i] = -1;
  } 

  for (i=0; i<MAP_LENGTH; i++)
  { int srcCount = -1;
    int srcIdx   = -1;
    while (srcCount < keySource[i])
    { srcIdx = (srcIdx + 1) % MAP_LENGTH;
      if (mapEncrypt[srcIdx] == -1) srcCount++;
    }   

    int snkCount = -1;
    int snkIdx   = -1;
    while (snkCount < keySink[i])
    { snkIdx = (snkIdx + 1) % MAP_LENGTH;
      if (mapDecrypt[snkIdx] == -1) snkCount++;
    }   

    mapEncrypt[srcIdx] = snkIdx;
    mapDecrypt[snkIdx] = srcIdx;

    if (action == ACTION_ENCRYPT || action == ACTION_ENCRYPT_STRONG ) map[srcIdx] = snkIdx;
    else map[snkIdx] = srcIdx;
  }

  if (DEBUG)
  { printf ("Map Encrypt: ");
    for (i=MAP_LENGTH-1; i>=0; i--) printf("%d ", mapEncrypt[i]);
    printf("\n");

    printf ("Map Decrypt: ");
    for (i=MAP_LENGTH-1; i>=0; i--) printf("%d ", mapDecrypt[i]);
    printf("\n\n");
  }
}



//==================================================================================
// encrypt 
//
// uses global array map to either encrypt or decrypt each character in global 
//   string inStr.
// Return 0 if no errors found.
//==================================================================================
int encrypt()
{ int i=0;
  while (inStr[i])
  { unsigned char c = inStr[i];
    unsigned char x = 0;
    int bitIdx;
    
    if (action == ACTION_ENCRYPT)
    { if (c < 32 || c > 126)
      { printf("Error** Illegal character in encryption string.\n");
        return -1;
      }
    }
    else
    { if (c < 32 || (c > 126 && c < 192) || c > 223)
      { printf("Error** Illegal character in decryption string: %d, %c\n", c,c);
        return -1;
      }
      if (c>=192) c-=192;
    }
        
    for (bitIdx=0; bitIdx<MAP_LENGTH; bitIdx++)
    { unsigned char bitMap = map[bitIdx];
      unsigned char srcBit = 1<<bitIdx;
      unsigned char snkBit = 1<<bitMap;
      if (c & srcBit) x = x | snkBit;
    }
    
    if (action == ACTION_ENCRYPT) { if (x<32) x+=192; } 
    outStr[i] = x; 
    i++;
  } 
  return 0;
}


//==================================================================================
// encryptStrong 
//==================================================================================
int encryptStrong()
{ int i=0;
  while (inStr[i])
  { unsigned char c = inStr[i];
    unsigned char x = 0;
    int bitIdx;
    
    if (c < 32 || c > 126)
    { printf("Error** Illegal character in encryption string.\n");
      return -1;
    }
    
    for (bitIdx=0; bitIdx<MAP_LENGTH; bitIdx++)
    { unsigned char bitMap = map[bitIdx];
      unsigned char srcBit = 1<<bitIdx;
      unsigned char snkBit = 1<<bitMap;
      if (c & srcBit) x = x | snkBit;
    }
   
    unsigned char lastSnkKeys = keySink[0]<<3 | keySink[1];
    if (DEBUG) printf("x = %d,  (%c),  lastSnkKeys=%d\n", x, x, lastSnkKeys);
    unsigned char nextSnkKeys = lastSnkKeys ^ x;
    keySink[0] = (nextSnkKeys & 56) >> 3;
    keySink[1] = nextSnkKeys & 7;
    if (DEBUG) 
    { printf("nextSnkKeys=%d  (%d, %d)\n", nextSnkKeys, keySink[0], keySink[1]);
    }
    buildMap();


 
    if (x<32) x+=192; 
    outStr[i] = x;

    i++;
  } 
  return 0;
}




//==================================================================================
// decryptStrong 
//==================================================================================
int decryptStrong()
{ int n=strlen(inStr)-1;
  int i;
  for (i=n; i>=0; i--)
  { unsigned char c = inStr[i];
    unsigned char x = 0;
    int bitIdx;
    
    if (c < 32 || (c > 126 && c < 192) || c > 223)
    { printf("Error** Illegal character in decryption string: %d, %c\n", c,c);
      return -1;
    }
    if (c>=192) c-=192;

    unsigned char lastSnkKeys = keySink[0]<<3 | keySink[1];
    if (DEBUG) printf("c = %d,  (%c),  lastSnkKeys=%d\n", c, c, lastSnkKeys);
    unsigned char nextSnkKeys = lastSnkKeys ^ c;
    keySink[0] = (nextSnkKeys & 56) >> 3;
    keySink[1] = nextSnkKeys & 7;
    if (DEBUG) 
    { printf("nextSnkKeys=%d  (%d, %d)\n", nextSnkKeys, keySink[0], keySink[1]);
    }
    buildMap();
 
    
    for (bitIdx=0; bitIdx<MAP_LENGTH; bitIdx++)
    { unsigned char bitMap = map[bitIdx];
      unsigned char srcBit = 1<<bitIdx;
      unsigned char snkBit = 1<<bitMap;
      if (c & srcBit) x = x | snkBit;
      if (DEBUG) printf("bitIdx=%d, bitMap=%d\n", bitIdx, bitMap);
    }
   
    outStr[i] = x;

  } 
  return 0;
}

//==================================================================================
// main 
//==================================================================================
int main(int argc, char *argv[])
{
  if (readParameters(argc, argv)) return -1;
  if (parseKey()) return -1;

  buildMap();
  if (action == ACTION_ENCRYPT_STRONG) 
  { if (encryptStrong()) return -1;
  }
  else if (action == ACTION_DECRYPT_STRONG) 
  { if (decryptStrong()) return -1;
  }
  else
  { if (encrypt()) return -1;
  }

  if (action == ACTION_ENCRYPT_STRONG) 
  { 
    int i;
    for (i=0; i<MAP_LENGTH-1; i++) printf("%d%d", keySource[i], keySink[i]);
    printf(" ");
  }
  printf("%s\n", outStr);

  return 0;
}

