/* solitaire.c */

/*
 * The Solitaire encryption algorithm programmed in C.
 * solitaire encryption system by Bruce Schneier
 * based on a deck of cards
 * See <http://www.counterpane.com/solitaire.html> for details.
 * programming by Lloyd Miller, Sept 2000
*/

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/*
 *   usage :
 *       to encrypt
 *              solitair -e key <plaintext >cyphertext
 *       to decrypt
 *              solitair -d key <cyphertext >plaintext
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <ctype.h>

#ifdef TEST_DATA

const char *key = "";
const char *ciphertext = "EXKYIZSGEHUNTIQ";
char knownOrder[54][4] = {
    "AC","2C","3C","4C","5C","6C","7C","8C","9C","10C","JC","QC","KC",
    "AD","2D","3D","4D","5D","6D","7D","8D","9D","10D","JD","QD","KD",
    "AH","2H","3H","4H","5H","6H","7H","8H","9H","10H","JH","QH","KH",
    "AS","2S","3S","4S","5S","6S","7S","8S","9S","","","","",
    "A","B"
};

#else

char *key = "IFENTROPYWINSOUTWARDLOOKSSHOULDLEAVEYOUCOLD";
char *ciphertext = "WBBMCHGFIBLXCQYWEZFLITHPJLFHWYETKWYLJOTYYNGYJBIOGIFUVMRXIHGURAGXHNQHRSXAWHUFJTAMSMMOSMVBAAKPGVVWXOVMYKZPLLUL";
char knownOrder[54][4] = {
    "","","","","","8D","AS","JC","","","","","9D",
    "","","","QS","","","","","10C","","8C","","",
    "","JD","","","","","QD","9H","QH","","","","",
    "","10H","10D","","","","","","","","","","",
    "10S","JH"
};

#endif
const char *bufferPos;


void usage(void)
{
  fprintf(stderr,
	  "usage:\n"
	  "       to encrypt\n"
	  "              solitair -e key <plaintext >cyphertext\n"
	  "       to decrypt\n"
	  "              solitair -d key <cyphertext >plaintext\n");
  exit(EXIT_FAILURE);
}

int getalpha(void)
{
  int a;

  while (1) {
    a = *bufferPos++;
    if (a == 0) {
        bufferPos = ciphertext;
        return 0;
    }
    if (a >= 'A' && a <= 'Z')
      return a;
    if (a >= 'a' && a <= 'z')
      return a - 'a' + 'A';
  }
}

int findit(char *deck, int val, int siz)
{
  int i = 0;
  while(siz--)
    {
      if (*deck == val)
	return i;
      deck++;
      i++;
    }
  return -1;
}

int step(char *deck)
{
  int c;
  int b;
  int a;
  char tmp[54];

  do {

    /* step 1 */

    c = findit(deck, 53, 54);
    if (c < 53)
      {
	a = c + 1;
	deck[c] = deck[a];
      }
    else
      {
	for (a = 53; a > 1; a--)
	  deck[a] = deck[a - 1];
	a = 1;
      }
    deck[a] = 53;

    /* step 2 */

    b = findit(deck, 54, 54);
    if (b < 52)
      {
	c = b + 1;
	deck[b] = deck[c];
	if (a == c)
	  a = b;
	b = c + 1;
	deck[c] = deck[b];
	if (a == b)
	  a = c;
      }
    else
      {
	c = b;
	b = b - 51;
	for (; c > b; c--)
	  {
	    deck[c] = deck[c - 1];
	    if (a == c - 1)
	      a = c;
	  }
      }
    deck[b] = 54;

    /* step 3 */

    if (a > b)
      {
	c = a;
	a = b;
	b = c;
      }
    tmp[53] = deck[b++];

    c = 0;
    while (b < 54)
      tmp[c++] = deck[b++];

    b = a;
    while (deck[b] != tmp[53])
      tmp[c++] = deck[b++];

    tmp[c++] = tmp[53];
    b = 0;
    while (b < a)
      tmp[c++] = deck[b++];

    assert(c == 54);

    /* step 4 */

    b = tmp[53];
    if (b == 54)
      b = 53;

    a = 0;
    for (c = b; c < 53; c++)
      deck[a++] = tmp[c];
    for (c = 0; c < b; c++)
      deck[a++] = tmp[c];
    assert(a == 53);

    deck[53] = tmp[53];

    /* step 5 */

    a = deck[0];
    if (a == 54)
      a = 53;
    a = deck[a];
  } while (a > 52);
  return (a - 1) % 26 + 1;
}

int sumchar(int a, int b)
{
  return 'A' + (toupper(a) - 'A' + b + 26) % 26;
}

int calculate(char deck[])
{
  int i, j;
  int mode = 1;  /* default mode is encode, non-zero is decode */
  const char *keyPos = key;

    /* do key */
    while (*keyPos)
	{
	  int c = *keyPos++;
	  char tmp[53];

	  if (c >= 'a' && c <= 'z')
	    c = c - 'a' + 'A';
	  if (c >= 'A' && c <= 'Z')
	    {
	      c = c - 'A' + 1;
	      step(deck);
	      i = 0;
	      for (j = c; j < 53; j++)
		tmp[i++] = deck[j];
	      for (j = 0; j < c; j++)
		tmp[i++] = deck[j];
	      for (j = 0; j < 53; j++)
		deck[j] = tmp[j];
	    }
	}

  j = 0;
  while ((i = getalpha()) != 0)
    {
      putchar(mode ? sumchar(i, -step(deck)) : sumchar(i, step(deck)));
      j++;
      if (j % 5 == 0)
	{
	  if (j == 50)
	    {
	      j = 0;
	      ;//putchar('\n');
	    }
	  else
	    ;//putchar (' ');
	}
    }

  j = j % 5;
  if (j)
    while (j < 5)
      {
	i = 'X';
	putchar(mode ? sumchar(i, -step(deck)) : sumchar(i, step(deck)));
	j++;
      }

  printf("\n");

  return EXIT_SUCCESS;
}

void stackDeck(char deck[], char remainder[]) {
    int i;
    for (i=0; i<54; i++)
        remainder[i] = i+1;
    for (i=0; i<54; i++) {
        char temp[4];
        int value = 0;
        strcpy(temp, knownOrder[i]);
        // Jokers
        if (strlen(temp) == 0)
            deck[i] = 0;
        else if (strcmp(temp, "A") == 0) {
            deck[i] = 53;
            value = 53;
        } else if (strcmp(temp, "B") == 0) {
            deck[i] = 54;
            value = 54;
        } else {
            // number
            if (temp[0] == 'A')
                value = 1;
            else if (temp[0] == '1')
                value = 10;
            else if (temp[0] == 'J')
                value = 11;
            else if (temp[0] == 'Q')
                value = 12;
            else if (temp[0] == 'K')
                value = 13;
            else
                value = temp[0] - '0';
            // suit
            switch (temp[strlen(temp) - 1]) {
                case 'C':
                    break;
                case 'D':
                    value += 13;
                    break;
                case 'H':
                    value += 26;
                    break;
                case 'S':
                    value += 39;
                    break;
            }
            deck[i] = value;
        }
        if (deck[i] != 0)
            remainder[value-1] = 0;
    }
}

void printDeck(char deck[]) {
    int i;
    for (i=0; i<54; i++) {
        int value = deck[i];
        int suit;
        if (value == 0)
            printf("?");
        else if (value == 53)
            printf("A");
        else if (value == 54)
            printf("B");
        else {
            value--;
            suit = value / 13;
            value = value % 13;
            value++;
            if (value == 1)
                printf("A");
            else if (value == 11)
                printf("J");
            else if (value == 12)
                printf("Q");
            else if (value == 13)
                printf("K");
            else
                printf("%d", value);
            switch(suit) {
                case 0: printf("C"); break;
                case 1: printf("D"); break;
                case 2: printf("H"); break;
                case 3: printf("S"); break;
            }
        }
        printf(" ");
    }
    printf("\n");
}

void recurse(char deck[], char remainder[]) {
    int i;
    int openPos = -1;
    int remainderCount;
    for (i=0; i<54; i++) {
        if (remainder[i] != 0)
            remainderCount++;
        if ((openPos == -1) && (deck[i] == 0))
            openPos = i;
    }
    if ((openPos == -1) || (remainderCount == 0)) {
        // Reached the end of recursion, try answer
        printDeck(deck);
        calculate(deck);
        return;
    }
    for (i=0; i<54; i++) {
        if (remainder[i] == 0)
            continue;
        // Try
        deck[openPos] = remainder[i];
        remainder[i] = 0;
        // Recurse
        recurse(deck, remainder);
        // Reverse out change
        remainder[i] = deck[openPos];
        deck[openPos] = 0;
        
    }
}

int main(int argc, char **argv) {
    int i;
    char deck[54], remainder[54];
    bufferPos = ciphertext;
//    for (i = 0; i < 54; i++) {
//        deck[i] = i + 1;
//    }
    stackDeck(deck, remainder);
    printDeck(deck);
    printDeck(remainder);
    recurse(deck, remainder);
    return 0;
}

/* end of file */
