/*
  Polyalphabetic substitution: Vigenère-Chiffre 
  http://www.ogobin.org/home/algorithms/chiffre_poly.php

  Operations modes:     Repetitive Key, Key Progression, Autoclave
  Algorithms:           Vigenère

  Copyright (C) 2003 Oliver Gobin
  Licence type: GNU General Public License 
  Author: Oliver Gobin <og@ogobin.org>
*/

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

char char_shift_int(char c, unsigned int i)
{
  char base = 0;
  if (i == 0) { return(c); }
  if (isupper(c)) { base = 'A'; }
  else if (islower(c)) { base = 'a'; }
  else { return(c); } // letters only
  c -= base;
  c = (c + i) % 26 + base;
  return(c);
}

char char_shift_char(char c, char c2) {
  char base = 0;
  char flag_abs = 0;

  if (isupper(c)) { base = 'A'; }
  else if (islower(c)) { base = 'a'; }
  else { return(c); } // letters only
  c -= base;

  if(c2 < 0) { c2 = abs(c2); flag_abs = 1; }
  if (isupper(c2)) { c2 -= 'A'; }
  else if (islower(c2)) { c2 -= 'a'; }
  else { return(c+base); }
  // decoding: P = C - K or P = C + abs(K-26)
  if(flag_abs) c2 = abs(c2 -= 26);

  c = (c + c2) % 26 + base;
  return(c);
}

char *vigenere(char *P, char *K, int mode) {
  /*
    P                   Plaintext
    K                   Key
    mode == (-)1        Repetitive Key
    mode == (-)2        Key Progression
    mode == (-)3        Autoclave
             ^ for decoding
  */

  char *C = (char *) malloc(sizeof(char) * strlen(P));
  const int k = strlen(K) - 1;
  char c;
  int k_i = 0;
  int i = 0;
  unsigned int keyrnd = 0; // for Key Progression Mode

  for(i = 0; i < strlen(P); i++) {
    switch(mode) {
      default:
      case -1:
      case 1:
        c = K[k_i];
        break;
      case -2:
      case 2:
        c = char_shift_int(K[k_i], keyrnd);
        break;
      case 3:
        if(i > k) {
          c = P[i-k-1];
        }
        else {
          c = K[k_i];
        }
        break;
      case -3:
        if(i > k) {
          c = C[i-k-1]; // Autoclave decoding: C is the old P!
        }
        else {
          c = K[k_i];
        }
        break;
    }
    if(mode < 0) c *= -1; // for decoding, c has to be negative
    C[i] = char_shift_char(P[i], c);
    k_i++;
    if(k_i > k) {
      k_i = 0;
      keyrnd++;
    }
  }
  return(C);
}

int main(int argc, char *argv[]) {
  char *Plaintext = "Hallo Welt, das ist schoen hier!";
  char *Chiffre;
  char *Key = "test";

  strcpy(Chiffre, vigenere(Plaintext, Key, 1));
  printf("%s\n", Chiffre);
  printf("%s\n", vigenere(Chiffre, Key, -1));

  strcpy(Chiffre, vigenere(Plaintext, Key, 2));
  printf("%s\n", Chiffre);
  printf("%s\n", vigenere(Chiffre, Key, -2));

  strcpy(Chiffre, vigenere(Plaintext, Key, 3));
  printf("%s\n", Chiffre);
  printf("%s\n", vigenere(Chiffre, Key, -3));

  return(0);
}