/******
	BinaryKaos
	----------
	Version:		0.2 (beta)
	Written by:		d1s4st3r
    Website:        http://xoomer.virgilio.it/mental_insomnia/
	Contact me at:	d1s4st3r_[at]_gmail_[dot]_com
******/


/*
 *  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 Library 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.
 */



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

#define		BINNUM		0x30


int pow(int x, int y);
char binarytochar(char *bin);
char swapdigits(char c);
char *chartobinary(char chr);
char *bintogenome(char *binstr);


/******
	this function shows program's quick help
******/

void showhelp()
{
	printf("Usage: binarykaos [-h] <options> <filename>\n");
	printf("\n");
	printf("Options:\n");
	printf("\n");
	printf("    -h, --help   shows this help\n");
	printf("\n");
	printf("    -c <num>     number of colums of binary octets\n");
	printf("                   (NOTE: standard UNIX terminal has 80 char columns,\n");
	printf("                    so if you want BinaryKaos' output to fit it right,\n");
	printf("                    set <num> to '10' (value goes from '1' to <num>)\n");
	printf("\n");
	printf("    --genome     prints a genome (ACGT) string from binary string\n");
	printf("                   (NOTE: proteins are assigned like this:\n");
	printf("                        binary couple \"00\" ---> \"A\"\n");
	printf("                        binary couple \"01\" ---> \"C\"\n");
	printf("                        binary couple \"10\" ---> \"G\"\n");
	printf("                        binary couple \"11\" ---> \"T\"\n");
	printf("                    no binary output will be printed)\n");
	printf("\n");
	printf("    -i           attempts inverse provedure to get original file\n");
	printf("                   (NOTE: BinaryKaos will attempt to convert a binary-digit\n");
	printf("                    file back to the original file (whether ASCII or binary),\n");
	printf("                    but this procedure is actually not totally safe)\n");
	printf("\n");
	printf("    -n <num>     number of digits in binary words\n");
	printf("                   (NOTE: only works after '-i' option, default <num> value is '8')\n");
	printf("\n");
	printf("    -s           swaps all 0s with 1s and viceversa\n");
	printf("\n");
	printf("----\n");
	printf("BinaryKaos v0.3 (beta)\n");
	printf("Written by d1s4st3r.\n");
	printf("Please report comments and bugs to <d1s4st3r@hotmail.com>.\n");
}


/******
	this function calculates the x^y power
******/

int pow(int x, int y)
{
	int res = x;
	int i;

	if (y<=0) return 1;

	for (i=1; i<y; i++)
		res = res * x;

	return res;
}


/******
	this function converts a char (1 byte) to an 8-char string of 0s and 1s (8 bytes)
******/

char *chartobinary(char chr)
{
	char *binstr = malloc(sizeof(char)*9);
	char *temp = malloc(sizeof(char)*9);
	int k = 0;
	int n = 0;
	int remain;
	int i;

	do
	{
		remain = chr % 2;
		chr = chr / 2;
		temp[k++] = remain + BINNUM;
	}
	while (chr>0);

	for (i=k; i<8; i++) // digits?
		temp[i] = 0 + BINNUM;
	temp[8] = '\0'; // digits?

	k = i;

	while (k>=0)
	{
		binstr[n++] = temp[--k];
	}

	binstr[n-1] = 0;

	return binstr;
}


/******
	this function converts an 8-char string of 0s and 1s (8 bytes) to a char (1 byte)
******/

char binarytochar(char *bin)
{
	int len = strlen(bin);
	unsigned int i;
	char chr = 0;

	for (i=0; i<len; i++)
		chr += ((bin[i]-BINNUM)*(pow(2,(7-i))));

	return chr;
}


/******
	this function converts 0s in 1s and viceversa
******/

char swapdigits(char c)
{
	if (c=='1') c = '0';
	else if (c=='0') c = '1';
	else c = '\0';

	return c;
}


/******
	this function converts a couple of binary digits into
    a human genome symbol (A, C, G or T)
******/

char *bintogenome(char *binstr)
{
	char *genome = (char *)malloc(sizeof(char)*5);
	int i;
	int j = 0;

	for (i=0; i<8; i=i+2)
	{
		if ((binstr[i]=='0')&&(binstr[i+1]=='0'))
		{
			genome[j] = 'A';
			j++;
		}
		else if ((binstr[i]=='0')&&(binstr[i+1]=='1'))
		{
			genome[j] = 'C';
			j++;
		}
		else if ((binstr[i]=='1')&&(binstr[i+1]=='0'))
		{
			genome[j] = 'G';
			j++;
		}
		else if ((binstr[i]=='1')&&(binstr[i+1]=='1'))
		{
			genome[j] = 'T';
			j++;
		}
	}

	genome[4] = '\0';

	return genome;
}


/******
	main()
******/

int main(int argc, char *argv[])
{
	FILE *fp;
	char *filename;
	char temp[9];
	int maxcols = 0;			/* for UNIX terminal width of 80 chars */
	int cpl = 0;				/* columns per line */
	int i, j = 0;				// counters
	int flag = 0;
	int digits = 8;				/* number of digits for binary sequence */

	/* initial command line check */
	if (argc<2)
	{
		printf("Usage: binarykaos [-h] <options> <filename>\n");
		printf("Try 'binarykaos -h' for more information\n");

		return EXIT_FAILURE;
	}
	else if ((argc==2)&&((!strcmp(argv[argc-1], "-h"))||(!strcmp(argv[argc-1], "--help"))))
	{
		showhelp();

        return EXIT_SUCCESS;
	}

	/* command line's arguments check and parsing */
	for (i=1; i<(argc-1); i++)
	{
		if (strcmp(argv[i], "-c")==0)
		{
	    	maxcols = atoi(argv[i+1]);
			if (maxcols<=0)
			{
				fprintf(stderr, "WARNING: Number of colums <= 0, set to '10' as default value.\n");
				maxcols = 10;
			}
		}
		else if (strcmp(argv[i], "-i")==0)
		{
			flag = 1;
		}
		else if (strcmp(argv[i], "-n")==0)
		{
			if (flag==1)
			{
				digits = atoi(argv[i+1]);
				if ((digits<=0)||(digits>8))
				{
					fprintf(stderr, "WARNING: Number of digits must be 0<n<9, set to '8' as default value.\n");
					digits = 8;
				}
			}
		}
		else if (strcmp(argv[i], "-s")==0)
		{
			flag = 2;
		}
		else if (strcmp(argv[i], "--genome")==0)
		{
			flag = 3;
		}
	}

	filename = argv[i];

	if ((fp=fopen(filename, "r"))==NULL)
	{
		fprintf(stderr, "Error while reading \"%s\"!\n", filename);

		return EXIT_FAILURE;
	}

	while (!feof(fp))
	{
		if (flag==0)
		{
			if (maxcols!=0)
			{
				if (cpl>=maxcols)
				{
					printf("\n");
					cpl = 0;
				}
				cpl++;
			}
			printf("%s", chartobinary(fgetc(fp)));
		}
		else if (flag==1)
		{
			if (j>(digits-1))
			{
				temp[digits] = '\0';
				printf("%c", binarytochar(temp));
				j = 0;
			}
			temp[j] = fgetc(fp);
			j++;
		}
		else if (flag==2)
		{
			printf("%c", swapdigits(fgetc(fp)));
		}
		else if (flag==3)
		{
			printf("%s", bintogenome(chartobinary(fgetc(fp))));
		}
		else
		{
			fprintf(stderr, "ERROR: Undefined operation!!!\n");

    		return EXIT_FAILURE;
		}
	}

	fclose(fp);

	return EXIT_SUCCESS;
}

