/*  Roll - a program bent on being able to roll any combonation of dice
    you can possibly think of.

    Copyright (C) 1999 Jeff Frasca <ph43drus@home.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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if the file "GPL" isn't at the root of the 
    source tree, write to: 
        the Free Software Foundation, Inc. 
        59 Temple Place, Suite 330
        Boston, MA  02111-1307  USA

    Happy Hacking :)
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include "asciidefs.h"

#define VERSION_STRING "1.0"
#define RAND_DEVICE "/dev/urandom"
#define ARBITRARY_NUMBER 8 /* keep this number below the log base 10 of
			     the maximum number a long can hold on your 
			     processor. 
			     */
#define ARBITRARYII 256

long rollDice(long, long);
long parseDice(char *, char *, int);
void printHelp(void);

main(int argc, char *argv[])
{
	long total; 
	int flagq = 0, flagp = 0, cont = 1, j = 1, 
	i = 0, k, ii, iii;
	char final[ARBITRARYII] = { '\0' }, name[ARBITRARYII] = { '\0' };
	
	/* getopt_long variables */
	int loptindex = 0, c;
	static struct option long_options[] = 
	{ 
		{"pedantic", 0, 0, 'p'},
		{"ircmode", 1, 0, 'i'},
		{"help", 0, 0, 'h'},
		/*{"palladium", 1, 0, 'p'},*/
		{0, 0, 0, 0}
	};
	
	while (1) {
		int this_optind = optind ? optind : 1;
		
		c = getopt_long(argc, argv, "i:ph", long_options,
				&loptindex);
		
		if (c == -1) {
			break;
		}
		
		switch (c) {
			case 'p':
				flagp = 1;
				break;
			case 'i':
				flagq = 1;
				if (strlen(optarg) < ARBITRARYII - 2) {
					memcpy(name, optarg, strlen(optarg));
				} 
				else {
					memcpy(name, optarg, ARBITRARYII - 2);
				}
				break;
			case 'h':
				printHelp();
				exit(0);
				break;
		}
	}


  /* Check to make sure there are enough args 
	if (argc < 2) {
		printf("Usage: roll [-q] ndx*s+b\n");
		printf("\tn -- number of times to roll\n");
		printf("\tx -- number of sides on dice\n");
		printf("\ts -- number to multiply the dice by\n");
		printf("\tb -- number to add to the roll\n\n");
		printf("\t-q -- irc mode for use with the dicebot\n\n");
		return 1;
	}

	for (k = 0; k < argc; k++) {
		if (argv[k][0] == '-' && argv[k][1] == 'q') {
			flagq = 1;
			j++;
		}
		if (argv[k][0] == '-' && argv[k][1] == 'p') {
			flagp = 1;
			j++;
		}
	}
	*/

	if (flagq != 1)
		printf("dice roller v%s\n", VERSION_STRING);

	if (optind < argc) {
		while (optind < argc) {
			total = parseDice(argv[optind++], final, flagp);

			switch (flagq) {
				case 0:
					printf("\nRolled: %s\nReturned: "
						"%ld\n\n", final, total);
					break;
				case 1:
					printf("%s rolled a %s and it came up: "
						"%ld\n", name, final, total);
					break;
				default:
					printf("\nNothing Risked, "
						"Nothing Gained\n\n");
					return 1;
			break;
			}
		}
	}
	else {
		printHelp();
	}

	return 0;
}

long parseDice(char *dice, char *rolled, int flagp) {
	int i, j, flag = 0, termflag = 0, dlength; 
	long x, n, total = 0, subtotal = 0;
	char tempn[ARBITRARY_NUMBER] = { '\0' };
	
	dlength = strlen(dice);

	if (flagp != 1) { /* we're ignoring operator precedence */
	    for (i = 0, j = 0; i <= dlength; i++, j++) {
		/*printf("dice[i]: %c, %d\n", dice[i], i);*/
		switch ((int)dice[i]) {
			case NADA: case EIN: case ZWEI: case DREI: case VIER:
			case FUNF: case SECHS: case SIEBEN: case ACHT:
			case NEUN:
				if (j > ARBITRARY_NUMBER - 1) {
					break;
				}
				tempn[j] = dice[i];
				break;
			case DAEY:
				tempn[j+1] = '\0';
				x = atol(tempn);
				/*printf("x: %ld tempn: %s\n", x, tempn);*/
				for (j = 0; j < ARBITRARY_NUMBER - 1; j++) {
					tempn[j] = '\0';
				}
				flag |= FDAEY;
				j = -1;
				break;
			case PLUS: case NEGA: case ASTR: case IX: case 0:
				tempn[j+1] = '\0';
				n = atol(tempn);
				/*printf("n: %ld j: %d\n", n, j);*/
				for (j = 0; j < ARBITRARY_NUMBER; j++) {
					tempn[j] = '\0';
				}
				j = -1;
				/*printf("flag: %d\n", flag);*/
				switch (flag) {
					case FPLUS:
						total += n;
						break;
					case FNEGA:
						total -= n;
						break;
					case FASTR: case FIX:
						total *= n;
						break;
					case FDAEY|FPLUS:
						total += rollDice(x, n);
						break;
					case FDAEY|FNEGA:
						total -= rollDice(x, n);
						break;
					case FDAEY|FASTR: case FDAEY|FIX:
						total *= rollDice(x, n);
						break;
					case FDAEY:
						total = rollDice(x, n);
						break;
					case 0:
						total = n;
						break;
				}
				flag = 0;
				switch ((int)dice[i]) {
					case PLUS:
						flag = FPLUS;
						break;
					case NEGA:
						flag = FNEGA;
						break;
					case ASTR: case IX:
						flag = FASTR;
						break;
					case 0:
						flag = 0;
						break;
				}
		}
		/*printf("j: %d tempn: %s\n", j, tempn);*/
		if (i < ARBITRARYII - 1) {
			rolled[i] = dice[i];
		}
	    }
	}
	else { /* we are using operator precedence */
	    for (i = 0, j = 0; i <= dlength; i++, j++) {
		/*printf("dice[i]: %c, %d\n", dice[i], i);*/
		switch ((int)dice[i]) {
			case NADA: case EIN: case ZWEI: case DREI: case VIER:
			case FUNF: case SECHS: case SIEBEN: case ACHT:
			case NEUN:
				if (j > ARBITRARY_NUMBER - 1) {
					break;
				}
				tempn[j] = dice[i];
				break;
			case DAEY:
				tempn[j+1] = '\0';
				x = atol(tempn);
				/*printf("x: %ld tempn: %s\n", x, tempn);*/
				for (j = 0; j < ARBITRARY_NUMBER - 1; j++) {
					tempn[j] = '\0';
				}
				flag |= FDAEY;
				j = -1;
				break;
			case ASTR: case IX: 
				tempn[j+1] = '\0';
				n = atol(tempn);
				/*printf("n: %ld j: %d\n", n, j);*/
				for (j = 0; j < ARBITRARY_NUMBER; j++) {
					tempn[j] = '\0';
				}
				j = -1;
				/*printf("flag: %d\n", flag);*/
				switch (flag) {
					case FPLUS:
					case FNEGA:
						subtotal = n;
						break;
					case FASTR: 
					case FIX:
						subtotal *= n;
						break;
					case FDAEY|FPLUS:
					case FDAEY|FNEGA:
						subtotal = rollDice(x, n);
						break;
					case FDAEY|FASTR: 
					case FDAEY|FIX:
						subtotal *= rollDice(x, n);
						break;
					case FDAEY:
						subtotal = rollDice(x, n);
						break;
					case 0:
						subtotal = n;
						break;
				}
				flag = 0;
				switch ((int)dice[i]) {
					case ASTR: 
						flag = FASTR;
						break;
					case IX:
						flag = FIX;
				}
				break;
			case PLUS: case NEGA: case 0:
				tempn[j+1] = '\0';
				n = atol(tempn);
				
				for (j = 0; j < ARBITRARY_NUMBER; j++) {
					tempn[j] = '\0';
				}
				j = -1;
				
				switch (termflag|flag) {
					case FPLUS|FIX: 
					case FPLUS|FASTR:
						total += (subtotal * n);
						subtotal = 0;
						break;
					case FNEGA|FASTR: 
					case FNEGA|FIX:
						total -= (subtotal * n);
						subtotal = 0;
						break;
					case FPLUS:
						total += n;
						break;
					case FNEGA:
						total -= n;
						break;
					case FDAEY:
						total = rollDice(x, n);
						break;
					case FDAEY|FPLUS|FASTR:
					case FDAEY|FPLUS|FIX:
						total += (subtotal * 
								rollDice(x,n));
						subtotal = 0;
						break;
					case FDAEY|FNEGA|FASTR:
					case FDAEY|FNEGA|FIX:
						total -= (subtotal * 
								rollDice(x,n));
						subtotal = 0;
						break;
					case FDAEY|FPLUS:
						total += rollDice(x, n);
						break;
					case FDAEY|FNEGA:
						total -= rollDice(x, n);
						break;
					case FASTR:
					case FIX:
						total = subtotal * n;
						subtotal = 0;
						break;
					case 0:
						total = n;
						break;
				}
				flag = 0;
				termflag = 0;
				switch ((int)dice[i]) {
					case PLUS:
						flag = FPLUS;
						termflag = FPLUS;
						break;
					case NEGA:
						flag = FNEGA;
						termflag = FNEGA;
						break;
					case 0:
						break;
				}

		}
		/*printf("j: %d tempn: %s\n", j, tempn);*/
		if (i < ARBITRARYII - 1) {
			rolled[i] = dice[i];
		}
	    }
	}
	
	return total;
}


/* calculates the roll */
long rollDice(long xtimes, long nsides)
{
	FILE *randomness;
	int total=0, i, rndnum;

	if ((randomness = fopen(RAND_DEVICE, "r")) == NULL) {
		printf("error: unable to open: " RAND_DEVICE "\n");
		exit(2);
	}
	
	for (i = 1; xtimes >= i; i++) {
		fread(&rndnum, sizeof(int), 1, randomness);
		if (rndnum < 0) { rndnum *= -1; }
		total += ((rndnum % nsides) + 1);
		/*printf("Total: %d: is %d\n", i, total);*/
	}

	return total;
}

void printHelp(void)
{
	printf("Usage:\n   %s %s\n",
		    "roll [-i name] [-p] [-h] [--ircmode name] [--pedantic]",
		    "[--help] <dice> [dice ...]");

	printf("\t<dice> is in the format of ndx, where n is the "
			"number of times to roll,\n");
	printf("\t\tand x is the number of sides the dice to roll has.\n");
	printf("\t\tYou can also add/subtract/multiply by constants "
			"or dice.\n");
	printf("\t\tThe default behavior is to ignore operator precedence.\n");
        printf("\t-i  IRC mode, requires the name arg, also --ircmode\n");
	printf("\t-p  Pedantic, this switch causes roll to give "
			"multiplication precedence\n");
	printf("\t\tover addition/subtraction (ie, it obeys long standing\n");
	printf("\t\tmathematical standards when -p is specified).\n");
	printf("\t\tAlso --pedantic\n");
	printf("\t-h  Prints this message, also --help\n\n");
	
}	
