#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <inttypes.h>
#include <wchar.h>
#include <stdarg.h>
#include <locale.h>
#include <time.h>

void Usage(void){
	printf("usage:\n\t./bitcsv filename.csv\n");
}

//Index,Date,Type,Info,Value,Balance

#define TRUE 0
#define FALSE 1
#define EARNED 0
#define SPENT 1
#define FEE 2
#define INVALID_TID 0

/*
struct Record{
	char *index;
	char *date;
	char *type;
	char *info;
	char *value;
	char *balance;
};
*/

uint32_t getdaynum(char *date){
    char tmp[3];
    tmp[0] = date[8];
    tmp[1] = date[9];
    tmp[3] = '\0';
    return((uint32_t)atoi((const char*)tmp));
}

struct Record{
	int type;
	uint64_t tid;
	double shares;
	double price;
	double value;
	char date[12];
};


int main(int argc, char **argv){
	if(argc != 2){
		Usage();
		exit(1);
	}
	//char *file_data_in  = NULL;

	const char numbers[10] = "0123456789";
	//const char numbers[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
	char *filename1  = (char *)malloc( sizeof(char) * strlen(argv[1]) + 1 );
    (void)strcpy(filename1, (const char *)argv[1]);
	//FILE *fp_data_in  = fopen( filename1, "r" );
	//if(fp_data_in  == NULL){
	//	printf("Error Opening %s Exiting\n", filename1);
	//	free(filename1);
	//	exit(1);
	//}

/*
	wchar_t *mem_file;
	struct stat buffer;
	int input = open(filename1, O_RDONLY);
	if(input == -1){
		printf("Error Opening %s Exiting\n", filename1);
		free(filename1);
		exit(1);
	}
	int status = fstat(input, &buffer);
	size_t file_bytes=buffer.st_size;
	mem_file=(wchar_t *)mmap(0, file_bytes, PROT_READ, MAP_SHARED, input, 0);
	if (mem_file == MAP_FAILED) {
		close(input);
		perror("Error mmapping the file");
		exit(EXIT_FAILURE);
	}
*/
	FILE *fp = fopen(filename1, "rb");
	if(!fp){
		printf("Error opening file: %s\n", filename1);
		free(filename1);
		exit(1);
	}

	uint32_t num1 = 0;
	uint32_t num2 = 0;
	uint32_t num3 = 0;
	//uint32_t isnum = 0;
	//size_t mmap_members = 0;
	size_t file_bytes = 0;
	size_t total_read = 0;
	uint8_t  match = 0;
	size_t index = 0;
	size_t largest_line = 0;
	char numbuf[32];
	char *linebuff;
	//uint32_t record_c = 0;
	//uint32_t field_c = 0;
	//uint32_t record_b_size = 0;
	//uint32_t field_b_size = 0;
	char tmpbuf[8];
	struct Record *record;
	size_t recordsize = 0;
	double tmp1 = 0;
	double tmp2 = 0;
	double balance = 0;
	char four_b[4];
	int inside_quote = 0;
	//union{ char a[4]; wchar_t b;}x;


//setlocale(LC_ALL, "POSIX");
/*
	num1 = 1;
	//get parameters
	while( fgets(tmpbuf, 8, fp_data_in) != NULL){
		//number of  earned/fee/spent records

		for(num3=0; tmpbuf[num3] != ','; num3++){
			isnum = 0;
			for(num4=0; num4<10; num4++){
				if(numbers[num4] == tmpbuf[num3])
					isnum = 1;
			}
			if(!isnum)
				break;
		}
		num2 = tmpbuf[
		num1++;


	}
	rewind(fp_data_in);

	//count number of newlines in file for structure size
	printf("file size %"PRIu64" bytes\n", file_bytes);
	mmap_members = file_bytes / sizeof(wchar_t);
	for(num1=0; num1<mmap_members; num1++){
		if(mem_file[num1] == L'\n')
			num2++;
	}
	record = (struct Record*)malloc(sizeof(struct Record)*num2);
 printf("mmap_members %"PRIu64"\n", mmap_members);
//1,"2013-11-29 05:30:30",earned,"BTC sold: [tid:1385703030392334] 0.31446905 BTC at $1,233.00000",387.74034,387.74034
	//for(num1=0; num1<file_bytes; num1++){
	num1 = 0;
*/

	fseek(fp , 0 , SEEK_END);
	file_bytes = ftell (fp);
	rewind(fp);
	printf("file_bytes %"PRIu64"\n", file_bytes);

	(void)memset((char *)four_b, '\0', 4);
//printf("'%ls'\n", 
	num2 = 0;
	while(total_read < file_bytes){
		total_read += fread(&four_b, 1, 1, fp);
//        x.a[0] = four_b[3];
//        x.a[1] = four_b[2];
//        x.a[2] = four_b[1];
//        x.a[3] = four_b[0];
		num2++;
		if(four_b[0] == '\n'){
			num1++;
			if(num2 > largest_line)
				largest_line = num2 + 1;
			num2 = 0;
		}
	}
	recordsize = num1;
//0022
	rewind(fp);
	printf("newlines %u\n", num1);
	record = (struct Record*)malloc(sizeof(struct Record)*recordsize);
	total_read = 0;
	linebuff  = (char *)malloc( sizeof(char) * largest_line + 1 );
	//(void)memset((char *)linebuff, '\0', sizeof(char) * largest_line + 1);
	(void)memset((char *)four_b, '\0', 4);
printf("here 1\n");
//printf("%02x %02x", mem_file[num1], L'"');
//65646e49 22
//wchar_t *wcptr = NULL;
	//while(num1<file_bytes){
		//wprintf(L"%c", mem_file[num1]);
		//total_read += fread(&four_b, 1, 4, fp);

	//skip first line
	while(total_read < file_bytes){
		total_read += fread(&four_b, 1, 1, fp);
		if(four_b[0] == '\n')
			break;
	}
printf("here 2 record %"PRIu64"\n", recordsize);


	index = 0;
	num1 = 0;
	while(index < recordsize){
		if(total_read >= file_bytes){
			break;
		}
		(void)memset((char *)record[index].date, '\0', sizeof(char) * 11);
		num1 = 0;
		(void)memset((char *)linebuff, '\0', sizeof(char) * largest_line + 1);
		for(num2 = 0; total_read < file_bytes; num2++){
			total_read += fread(&four_b, 1, 1, fp);
			linebuff[num2] = four_b[0];
			if(four_b[0] == '\n'){
				num1++;
				break;
			}
		}
		//verify record is valid
		for(num3=0; linebuff[num3] != '\0'; num3++){
			if(linebuff[num3] == ',')
				num2++;
		}
		if(num2<5)
			continue;
		if(linebuff[0] == '"')
			continue;
//printf("here 3\n");
		num2 = 0;
		num3 = 0;
		//grab date
		while(linebuff[num2] != ',')
			num2++;
		num2++;
		num3 = 0;
//printf("here 3.5\n");
		while(linebuff[num2] != ','){
			record[index].date[num3] = linebuff[num2];
			num3++;
			num2++;
		}
		//count 2 commas then grab type
		//for(num2 = 0; num3<2; num2++){
		//	if(linebuff[num2] == ','){
		//		num3++;
		//	}
		//}
//printf("here 4\n");
		num2++;

		//x.a[0] = four_b[0];
		//x.a[1] = four_b[1];
		//x.a[2] = four_b[2];
		//x.a[3] = four_b[3];
		//printf("read %"PRIu64"\n", total_read);
//		wprintf (L"%c\n", (wchar_t)x.b);
		//printf("%02x", mem_file[num1]);
		//printf("%u\n", num1);
/*
		if(mem_file[num1] != L"\""){
//if(wcscmp((const wchar_t *)&mem_file[num1], (const wchar_t *)&test ) != 0){
//wmemchr(in, L'A', 6);
			num1++;
			continue;
		}
printf("here 1\n");
		while(mem_file[num1] != L','){
			num1++;
		}
		num1++;
*/
		(void)memset((char *)tmpbuf, '\0', 8);
		for(num3=0; linebuff[num2] != ','; num3++){
			tmpbuf[num3] = linebuff[num2];
			num2++;
		}

		match = FALSE;
		//if not earned/spent/fee go to next newline and start over
		if(strcmp((const char*)tmpbuf, "earned" ) == 0){
			match = TRUE;
			record[index].type = EARNED;
		}
		if(strcmp((const char*)tmpbuf, "spent" ) == 0){
			match = TRUE;
			record[index].type = SPENT;
		}
		if(strcmp((const char*)tmpbuf, "fee" ) == 0){
			match = TRUE;
			record[index].type = FEE;
		}
		//if no match go to next newline and start over
		if(match == FALSE){
			//while(mem_file[num1] != L'\n')
			//	num1++;
			//index++;
			continue;
		}

//printf("index type: %u\n", record[index].type);

		//record if quotes around field
		if(linebuff[num2+1] == '"'){
			inside_quote = 1;
		}else{
			inside_quote = 0;
		}
		//num2 = 0;
		//get tid
		(void)memset(numbuf, '\0', 32);
		while(linebuff[num1] != '['){
			num1++;
		}
		num1 += 5;
//printf("here 2\n");
		for(num2=0; linebuff[num1] != ']'; num2++){
			numbuf[num2] = linebuff[num1];
			num1++;
		}

		record[index].tid = atof((const char*)numbuf);
//printf("here 2\n");
		//get shares
		num1 += 2;
		(void)memset(numbuf, '\0', 32);
		for(num2=0; linebuff[num1] != ' '; num2++){
			numbuf[num2] = linebuff[num1];
			num1++;
		}
		record[index].shares = atof((const char*)numbuf);
//printf("here 3\n");
		//get price
		(void)memset(numbuf, '\0', 32);
		while(linebuff[num1] != '$'){
			num1++;
		}
//printf("here 4 num1 = %u\n", num1);
		num1++;
		num2 = 0;
		//if(record[index].type == EARNED){

		//}
		//if(record[index].type == SPENT){

		//}
		//if(record[index].type == FEE){

		//}
		if(inside_quote){
			while(linebuff[num1] != '"'){
				if(linebuff[num1] == ','){
					num1++;
					continue;
				}
				numbuf[num2] = linebuff[num1];
				num1++;
				num2++;
			}
		}else{
			while(linebuff[num1] != ',' && linebuff[num1] != ' '){
				numbuf[num2] = linebuff[num1];
				num1++;
				num2++;
			}
		}
		//while(linebuff[num1] != '"' && linebuff[num1] != ',' && linebuff[num1] != ' '){
			//ignore comma
			//if(linebuff[num1] == ','){
			//	if(linebuff[num1-6] == '.')
			//		break;
			//	else
			//		num1++;

			//}else if(linebuff[num1] == ' '){
			//	break;
			//}
		//	numbuf[num2] = linebuff[num1];
		//	num1++;
		//	num2++;
		//}
		record[index].price = atof((const char*)numbuf);
//printf("here 5\n");
		//get value
		while(linebuff[num1] != ',')
			num1++;
		num1++;
		(void)memset(numbuf, '\0', 32);

		inside_quote = 0;
		if(linebuff[num1] == '"'){
			num1++;
			for(num2=0; linebuff[num1] != '"'; num2++){
				//ignore commas
				if(linebuff[num1] == ','){
					num1++;
					continue;
				}
				numbuf[num2] = linebuff[num1];
				num1++;
			}
		}else{
			for(num2=0; linebuff[num1] != ','; num2++){
				numbuf[num2] = linebuff[num1];
				num1++;
			}
		}
		record[index].value = atof((const char*)numbuf);

		//while(mem_file[num1] != '\n')
		//	num1++;
		index++;
		//printf("added %u\n", index);
//printf("here 5 index %"PRIu64"\n", index);

	}


//printf("here 6\n");
	printf("added %"PRIu64"\n", index);

	int buying = 1;
	double shares_total = 0;
	double fees_total = 0;
	double last_balance = 0;
	double start_balance = 0;
	balance = 0;
	//grab initial balance
	for(num1=0; num1<index; num1++){
		if(record[num1].type == EARNED){
			balance += record[num1].shares * record[num1].price;
			//printf("%lf %lf\n", record[num1].shares, record[num1].price);
			for(num2=0; num2<index; num2++){
				if(record[num1].tid == record[num2].tid){
					if(record[num2].type == FEE){
						fees_total += record[num2].value;
						//balance += record[num1].shares * record[num1].price;
						balance -= record[num2].value;
					}
				}
			}
printf("%u %"PRIu64" %lf %lf %lf %s\n", record[num1].type, record[num1].tid, record[num1].shares, record[num1].price, record[num1].value, record[num1].date);
			continue;
		}
		break;
	}
printf("\n");
	start_balance = balance;
	last_balance = balance;
	//printf("start balance: %lf\n", last_balance);
	//double day_balance = 0;
	uint32_t daypre = getdaynum(record[num1-1].date);
	uint32_t daycur = 0;
	//calculate the gain/loss percentage by day
	//for(;num1<index; num1++){
/*		
struct Record{
    int type;
    uint64_t tid;
    double shares;
    double price;
    double value;
    char date[12];
};
*/

	for(;num1<index; num1++){
//		printf("%u %"PRIu64" %lf %lf %lf %s\n", record[num1].type, record[num1].tid, record[num1].shares, record[num1].price, record[num1].value, record[num1].date);
		//printf("here a\n");
		//printf("%u\n", daycur);

		//daycur = getdaynum(record[num1].date);
//		if(daycur != daypre){
			//printf("%s %lf\n", record[num1].date, balance);
		//}
/*
		if(shares_total < 0.9){
			//daycur = getdaynum(record[num1].date);
			if(balance > last_balance){
				tmp1 = 1 - last_balance/balance;
				tmp1 *= 100;
			}else{
				tmp1 = 1 - balance/last_balance;
				tmp1 *= 100;
			}
			
			printf("%s %lf\n", record[num1].date, tmp1);
			//daypre = daycur;
			last_balance = balance;
		}
*/

		if(buying){
			if(record[num1].type == FEE){
				//num1++;
				continue;
			}
			if(record[num1].type == SPENT){
				balance -= record[num1].shares * record[num1].price;
shares_total += record[num1].shares;
				for(num2=0; num2<index; num2++){
					if(record[num1].tid == record[num2].tid){
						if(record[num2].type == FEE){
							fees_total += record[num2].value;
							balance -= record[num2].value;
						}
					}
				}
printf("%u %"PRIu64" %lf %lf %lf %s\n", record[num1].type, record[num1].tid, record[num1].shares, record[num1].price, record[num1].value, record[num1].date);
				continue;
			}
			if(record[num1].type == EARNED){
				buying = 0;
				num1--;
printf("\n");
				continue;
			}
		}
		if(!buying){
			if(record[num1].type == FEE){
				//num1++;
				continue;
			}
			if(record[num1].type == EARNED){
shares_total -= record[num1].shares;
				balance += record[num1].shares * record[num1].price;
				for(num2=0; num2<index; num2++){
					if(record[num1].tid == record[num2].tid){
						if(record[num2].type == FEE){
							fees_total += record[num2].value;
							balance -= record[num2].value;
						}
					}
				}
printf("%u %"PRIu64" %lf %lf %lf %s\n", record[num1].type, record[num1].tid, record[num1].shares, record[num1].price, record[num1].value, record[num1].date);
				continue;
			}
			if(record[num1].type == SPENT){
				buying = 1;
/*
        daycur = getdaynum(record[num1].date);
        if(daycur != daypre){
            if(balance > last_balance){
                tmp1 = 1 - last_balance/balance;
            }else{
                tmp1 = 1 - balance/last_balance;
            }
            tmp1 *= 100;
            printf("%s %lf\n", record[num1].date, tmp1);
            daypre = getdaynum(record[num1].date);
            last_balance = balance;
        }
*/
				//tmp1 = balance - last_balance;
				//tmp2 = tmp1 + last_balance;
				/*
				if(balance > last_balance){
					tmp1 = 1 - last_balance/balance;
					tmp1 *= 100;
				}else{
					tmp1 = 1 - balance/last_balance;
					tmp1 *= -100;
				}
				//tmp1 = 1 - last_balance/balance;
				//tmp1 *= 100;
				printf("gain/loss: %lf\n", tmp1);

				last_balance = balance;
				*/
				num1--;
printf("\n");
				continue;
			}
		}
		


	}
	//print last entry
	tmp1 = 1 - last_balance/balance;
	tmp1 *= 100;
//	printf("gain/loss: %lf\n", tmp1);

	printf("total fees %lf\n", fees_total);

	tmp1 = 1 - start_balance/balance;
	tmp1 *= 100;
	printf("total gain/loss: %lf\n", tmp1);



	/*
	//parse/print
	for(num1=0; num1<index; num1++){
		if(record[num1].type == EARNED){
			printf("earned: ");
			balance += record[num1].shares * record[num1].price;
		}
		if(record[num1].type == SPENT){
			printf("spent: ");
			balance -= record[num1].shares * record[num1].price;
		}
		if(record[num1].type == FEE){
			printf("fee: ");
			balance -= record[num1].value;
		}
		printf("tid %"PRIu64" shares %lf price %lf value %lf\n", record[num1].tid, record[num1].shares, record[num1].price, record[num1].value);

	}
	*/
	printf("balance: %lf\n", balance);
//fprintf(stdout, "%u\n", (unsigned)time(NULL));
//fprintf(stdout, "%u\n", (unsigned)time("2014-01-05"));
	//if(munmap(mem_file, file_bytes) == -1){
	//	printf("Error un-mmapping the file");
	//}
	free(record);
	//close(input);
	fclose(fp);
	free(filename1);
	free(linebuff);
	return 0;
}