/*
	bednautils 0.3
	Anicka Bernathova <anicka@anicka.net>, Hlavni Medved, April 2008 
	Licensed under the NWL - No Whining License.
	You may use this, modify this, redistribute this provided you agree to:
	- not whine about it;
	- the fact that there is no warranty of any kind;
	- retain this copyright in full.
*/

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

#define OFFSET 0.005

static void
err(char* msg, ...)
{
        va_list args;

        va_start(args, msg);
        vfprintf(stderr, msg, args);
        fputc('\n', stderr);
        va_end(args);
        exit(1);
}

static void
usage(void)
{
	err("Usage:\n\
p ciphertext key -> adds key to the ciphertext and prints all rotations\n\
m ciphertext key -> subtracts key from the ciphertext and prints all rotations\n\
r ciphertext     -> prints rot-1..rot-26\n\
c ciphertext     -> counts frequency of characters and compares to the Czech frequency\n\
a [start end]	 -> print numbered alphabet\n\
\n\
Use `-' instead of a string to read it from stdin\n\
Capitalize the command to get an alphabet numbered from 0");
}

static int abc_base = 1;

static int a2n(int c, int *bigp)
{
	int big = 0;
	if (c >= 'A' && c <= 'Z')
		c -= 'A', big=1;
	else if (c >= 'a' && c <= 'z')
		c -= 'a';
	else
	  	return -1;
	if (bigp)
		*bigp = big;
	return abc_base + c;
}

static int n2a(int c, int big)
{
	c = (c + 26 - abc_base) % 26;
	return c + (big ? 'A' : 'a');
}

static void
plus(char* ciph, char* key)
{
	int i=0, j=0;
	
	while (ciph[i]) {
		int x, y, big=0;

		x = a2n(ciph[i], &big);
		if (x >= 0) {
			if (!key[j])
				j = 0;
			y = a2n(key[j++], NULL);
			putchar(n2a(x+y, big));
		} else
			putchar(ciph[i]);
		i++;
	}
	putchar('\n');
}

static void
minus(char* ciph, char* key)
{
	int i=0, j=0;
	
	while (ciph[i]) {
		int x, y, big=0;

		x = a2n(ciph[i], &big);
		if (x >= 0) {
			if (!key[j])
				j = 0;
			y = a2n(key[j++], NULL);
			putchar(n2a(x-y, big));
		} else
			putchar(ciph[i]);
		i++;
	}
	putchar('\n');
}

static void
allop(char* ciph, char* key, void (*func)(char*, char *))
{
	int len, i, temp;
	
	len=strlen(key);

	for (i=0;i<len;i++){
		func(ciph,key);
		temp=key[0];
		memmove(key, key+1, len-1);
		key[len-1]=temp;
	}	
}

static void
rotate(char* ciph)
{
	char key[2] = {};

	for(key[0]='a'; key[0]<='z'; key[0]++){
		plus(ciph,key);
	}
}

static char*
getinput(void)
{
	char* input;
	int len, cur;
	char c;

	len=100;
	cur=0;

	if (!(input=malloc(len+1)))
		err("Low memory");
	while ((c=fgetc(stdin))>0){
		if (cur>=len){
			if (!(input=realloc(input,len*2+1)))
				err("Low memory");
			len*=2;
		}
		input[cur++]=c;
	}
	if (cur > 0 && input[cur-1] == '\n')
		cur--;
	input[cur]=0;

	return input;	
}

static void
count(char* input)
{
	int i, j, len, max;
	int counts[26] = {};
	double czech[26] = {	0.09420,  0.01711,  0.02791,  0.03719,
				0.10602,  0.00197,  0.00186,  0.02911,
				0.06473,  0.02069,  0.03795,  0.06003,
				0.03513,  0.05407,  0.07984,  0.03105,
				0.00001,  0.05247,  0.05601,  0.04957,
				0.03742,  0.04020,  0.00071,  0.00016,
				0.03145,  0.03314
			   };

	i=len=0;
	abc_base=0;
	while (input[i]>0){
		int x = a2n(input[i++], NULL);
		if (x >= 0) {
			counts[x]++;
			len++;
		}	
	}
	if (!len)
		err("No data, heh?");

	max=0;
	for (i=0;i<26;i++)
		if (counts[i]>max)
			max = counts[i];

	for (i=0;i<26;i++){
		double is=(double)counts[i]/len;
		int hsize = 32;
		char hist[hsize+1];

		for (j=0; j<hsize*counts[i]/max; j++)
			hist[j] = '*';
		hist[j] = 0;

		printf("%c: %3d/%3d = %.5f  %-*s  ",i+'a',counts[i],len,is,hsize,hist);
		printf("might be: ");

		for (j=0;j<26;j++){
			if (fabs(czech[j]-is)<OFFSET)
				putchar(j+'a');
		}

		printf("\n");
	}
}

static char*
in(char *c)
{
	if (strcmp(c, "-"))
		return c;
	else
		return getinput();
}

static void
print_alphabet(int start, int end)
{
	int i;

	if (start>end)
		err("Wtf?");

	for (i=start; i<=end; i++){
		printf("%3d: %c\n",i,((i-start)%26)+'A');
	}

}

int
main(int argc, char** argv)
{
	int action;

	if (argc<2 || !argv[1][0] || argv[1][1])
		usage();

	action=argv[1][0];
	if (action >= 'A' && action <= 'Z') {
		action += 32;
		abc_base = 0;
	}
	switch (action){
		case 'p':
			if (argc != 4)
				usage();
			allop(in(argv[2]), in(argv[3]), plus);
			break;
		case 'm':
			if (argc != 4)
                                usage();
                        allop(in(argv[2]), in(argv[3]), minus);
                        break;
		case 'r':
			if (argc != 3)
                                usage();
			rotate(in(argv[2]));
			break;
		case 'c':
			if (argc != 3)
                                usage();
			count(in(argv[2]));
			break;
		case 'a':
			if (argc == 2)
				print_alphabet(abc_base, abc_base+25);
			else if (argc == 4)
				print_alphabet(atoi(argv[2]),atoi(argv[3]));
			else
				usage();
			break;
		default:
			usage();	
	}

	return 0;
}
