#include "xbill.h"
#include "widgets.h"
#include "pixmaps.h"

/*GLOBALS*/	
Pixmap offscreen;
Dimension width, height;
GC gc;
Display *display;
Drawable window, rootwindow, mainwindow;
Colormap colormap;
Screen *screen;
XtAppContext app;
XGCValues values;
XpmAttributes attr;
int depth;
unsigned iteration;
int state;
horde bill;
network net;
library systems;
scorelist scores[10];
Pixmap defaultcursor, downcursor;
Pixmap about, rules, thanks, story, freeware;
Pixmap icon, logo;
extern Widget toplevel, base, menubar, field, info;
extern Widget scorebox, highscorebox;
extern Widget endgamebox, enternamebox;
extern Widget pausebutton;
int in_window, level, score;
int efficiency;
XtIntervalId timer;
int in_popup=0;
int slow=0;

/* CODE */

/*inits net.computers array and sets up computers*/
void setup_network(network *net){	
	int i, j, flag, counter=0;	
	/*sets up network for each level*/
	for (i=0; i<net->units; i++) {
		flag=0;
		while (!flag) {
			if (++counter>=4000) {
				net->units = i-1;
				return;
			}
			net->computers[i].x = 
				RAND(width/BORDER, width-width/BORDER-net->width);
			net->computers[i].y = 
				RAND(height/BORDER, height-height/BORDER-net->height);
			flag=1;
			/*checks for conflicting computer placement*/
			for (j=0; j<i && flag; j++)
				if (INTERSECT(net->computers[i].x, 
					net->computers[i].y,
					net->width-BILL_X_OFFSET+bill.width,
					net->height, net->computers[j].x,
					net->computers[j].y,
					net->width-BILL_X_OFFSET+bill.width,
					net->height))
					 flag=0;
		}
		net->computers[i].os= RAND(1, NUM_OS);
		net->computers[i].picture =
			net->pictures[net->computers[i].os][BASE_STATE];
		net->computers[i].status=BASE_STATE;
	}
}

/*redraws the computers at their location with the proper image*/
void update_network (network *net) {
	int i;
	for (i=0; i<net->units; i++) {
		XCopyArea(display, net->computers[i].picture, offscreen, gc,
			0, 0, net->width, net->height,
			net->computers[i].x,
			net->computers[i].y);/*offset no longer necessary*/
		net->computers[i].busy=0;
	}
}

void convert_to_toasters (network *net) {
	int i;
	for (i=0; i<net->units; i++) 
		net->computers[i].picture = net->pictures[0][OFF_STATE];
}

/*redraws offscreen with clean rectangle and borders*/
void clean_offscreen(int fg, int bg) {
	XSetForeground(display, gc, bg);
	XFillRectangle(display, offscreen, gc, 0, 0,
		width, height);
	XSetForeground(display, gc, fg);
}

/*copies offscreen to field*/
void refresh_field(Widget field) {
	XCopyArea(display, offscreen, window, gc, 0, 0, width, height, 0, 0);
}

/*fixes window size*/
void fix_window_size() {
	XSizeHints hints;
	Dimension width, height;
	Arg wargs[2];
	XtSetArg (wargs[0], XtNwidth, &width);
	XtSetArg (wargs[1], XtNheight, &height);
	XtGetValues(toplevel, wargs, 2);
	hints.width=width; hints.height=height;
	hints.base_width=width; hints.base_height=height;
	hints.min_width=width; hints.min_height=height;
	hints.max_width=width; hints.max_height=height;
	hints.flags = USSize|PSize|PMaxSize|PMinSize;
	XSetNormalHints(display, XtWindow(toplevel) ,&hints);
}

void get_border_location (horde *bill, int *x, int *y) {
	int i=RAND(0,3);
	if (i==0 || i==2) *x= RAND(0, width-bill->width);
	if (i==1 || i==3) *y= RAND(0, height-bill->height);
	if (i==0) *y=-bill->height-16;
	if (i==1) *x=width+1;
	if (i==2) *y=width+1;
	if (i==3) *x=-bill->width-2;
}

/*  Moves bill toward his target - returns whether or not he moved */
int move_bill (monster *guy, horde *bill, int level, int mode) {
	int xdist = guy->target_x - guy->x;
	int ydist = guy->target_y - guy->y;
	int step = step_size(level);
	int dx, dy;
	int sx = (abs(xdist) == xdist) * 2 - 1;
	int sy = (abs(ydist) == ydist) * 2 - 1;
	if (!xdist && !ydist) return 0;
	else if ( abs(xdist)<step && abs(ydist)<step ) {
		guy->x = guy->target_x;
		guy->y = guy->target_y;
	}
	else {
		xdist = abs(xdist);
		ydist = abs(ydist);
		switch(mode) {
			case SLOW:
				dx = (xdist*step*sx)/(xdist+ydist);
				dy = (ydist*step*sy)/(xdist+ydist);
				break;
			case FAST:
				dx = (xdist*step*sx*5)/(4*(xdist+ydist));
				dy = (ydist*step*sy*5)/(4*(xdist+ydist));
				break;
		}
		guy->x+=dx;
		guy->y+=dy;
		if (dx<0) {
			guy->cels = bill->lcels;
			guy->contexts = bill->lcontexts;
		}
		else if (dx>0) {
			guy->cels = bill->rcels;
			guy->contexts = bill->rcontexts;
		}
	}
	return 1;
}

/*  Adds a bill to the in list*/
void add_in(horde *bill, network *net, library *systems, int x, int y){
	static int id=0;
	monster *temp = (monster*) malloc (sizeof(monster));
	temp->x = x;
	temp->y = y;
	temp->index = 0;
	temp->cels = bill->lcels;
	temp->contexts = bill->lcontexts;
	temp->cargo = systems->os[WINGDOWS];
	temp->x_offset = -2;
	temp->y_offset = -15;
	temp->target_c = RAND(0, net->units-1);
	temp->target_x = net->computers[temp->target_c].x+net->width-BILL_X_OFFSET;
	temp->target_y = net->computers[temp->target_c].y+BILL_Y_OFFSET;
	temp->id = id++;
	bill->on_screen++; bill->off_screen--;
	insert(temp, &bill->in);
}

/*  Launches Bills whenever called  */
void launch_bills(horde *bill, network *net, library *systems, int max){
	int i, n, x, y;
	n=RAND(1, max);
	if (n > bill->off_screen) n = bill->off_screen;
	for (i=0; i<n; i++) {
		get_border_location(bill, &x, &y);
		add_in(bill, net, systems, x, y);
	}
}

/*  Update monster position */	
void update_in(horde *bill, network *net, library *systems, int level) {
	int dx, dy, moved;
	node *temp = bill->in;
	if (empty(bill->in)) return;
	while (temp){
		moved = move_bill(temp->data, bill, level, SLOW);
		if (!moved &&
			!(net->computers[temp->data->target_c].status == WINGDOWS_STATE) &&
			!(net->computers[temp->data->target_c].busy))
		{
			node *travel = temp;
			XSetClipOrigin(display,
				temp->data->contexts[temp->data->index],
				temp->data->x,temp->data->y);
			XCopyArea(display, temp->data->cargo, offscreen, gc,
				0, 0, systems->width, systems->height,
				temp->data->x+temp->data->x_offset,
				temp->data->y+temp->data->y_offset);
			XCopyArea(display, temp->data->cels[temp->data->index],
				offscreen,
				temp->data->contexts[temp->data->index],
				0, 0, bill->width, bill->height,
				temp->data->x, temp->data->y); 
			temp = travel->next;
			
			net->computers[travel->data->target_c].busy=1;
			travel->data->cels = bill->acels;
			travel->data->contexts = bill->acontexts;
			if (net->computers[travel->data->target_c].status == OFF_STATE)
				travel->data->index=6;
			else
				travel->data->index=0;
			move(travel, &bill->in, &bill->at);
			continue;
		}
		else if (!moved) {
			int i;
			do
				i=RAND(0, net->units-1);
			while (i == temp->data->target_c);
			temp->data->target_c=i;
			temp->data->target_x=net->computers[temp->data->target_c].x+net->width-BILL_X_OFFSET;
			temp->data->target_y=net->computers[temp->data->target_c].y+BILL_Y_OFFSET;
		}
		temp->data->index++;
		temp->data->index%=WALK_CELS;
		temp->data->y_offset+=(8*(temp->data->index%2)-4);
		XSetClipOrigin(display,temp->data->contexts[temp->data->index],
			temp->data->x,temp->data->y);
		XCopyArea(display, temp->data->cargo, offscreen, gc, 0, 0,
			systems->width, systems->height, temp->data->x+temp->data->x_offset,
			temp->data->y+temp->data->y_offset);
		XCopyArea(display, temp->data->cels[temp->data->index], offscreen, 
			temp->data->contexts[temp->data->index], 0, 0, bill->width, 
			bill->height, temp->data->x, temp->data->y); 
		temp=temp->next;
	}
}

/*  Update Bills standing at computers  */
void update_at(horde *bill, network *net, library *systems, int level){
	int sx;
	int sy;
	node *temp = bill->at;
	if (empty(bill->at)) return;
	while (temp) {
		if (temp->data->index == 13) {
			node *travel = temp;
			temp = travel->next;
			travel->data->y_offset = -15;
			travel->data->x_offset = -2;
			
			get_border_location(bill, &travel->data->target_x,
				&travel->data->target_y);
			travel->data->index = 0;
			travel->data->cels = bill->lcels;
			travel->data->contexts = bill->lcontexts;
			move(travel, &bill->at, &bill->out);
		}
		else {
			switch (temp->data->index) {
			/*massive hack; don't let your kids see this!*/
			case 0 : temp->data->y_offset =  bill->aheight - systems->height; break;
			case 1 : temp->data->x -= 8; temp->data->x_offset +=8; break;
			case 2 : temp->data->x -= 8; temp->data->x_offset +=8; break;
			case 3 : temp->data->x -= 10; temp->data->x_offset +=10; break;
			case 4 : temp->data->x += 3; temp->data->x_offset -=3; break;
			case 5 : temp->data->x += 2; temp->data->x_offset -=2; break;
			case 6 : if (net->computers[temp->data->target_c].status==BASE_STATE) 
					{net->base--; net->off++;}
				else {
					node *os = systems->strays;
					while (os) {
						if (os->data->target_c == temp->data->target_c) {
							delete(os, &systems->strays);
							break;
						}
						os=os->next;
					}
				}
				net->computers[temp->data->target_c].status = OFF_STATE;
				net->computers[temp->data->target_c].picture =
					net->pictures[net->computers[temp->data->target_c].os][OFF_STATE];
				temp->data->cargo = systems->os[net->computers[temp->data->target_c].os];
				temp->data->y_offset = -15;
				temp->data->x += 20; temp->data->x_offset -=20; break;
				sx = temp->data->x_offset;
				sy = temp->data->y_offset;
				break;
			case 7 : sy = temp->data->y_offset = bill->aheight - systems->height; sx = -2; break;
			case 8 : sy = -15; sx = -2; break;
			case 9 : sy = -7; sx = -7; temp->data->x -= 8; temp->data->x_offset +=8; break;	
			case 10 : sy = 0; sx = -7; temp->data->x -= 15; temp->data->x_offset +=15; break;
			case 11 : sy = 0; sx = -7;
				net->computers[temp->data->target_c].status = WINGDOWS_STATE;
				net->off--; net->nt++;
				net->computers[temp->data->target_c].picture = 
					net->pictures[net->computers[temp->data->target_c].os][WINGDOWS_STATE];
				break;
			case 12 :
				temp->data->x += 11; temp->data->x_offset -=11; break;
			}
			net->computers[temp->data->target_c].busy=1;
			if (temp->data->index>5 && temp->data->index<12){
			XCopyArea(display, systems->os[0]/*temp->data->cargo*/, offscreen, gc, 0, 0,
				systems->width, systems->height, temp->data->x+sx,
				temp->data->y+sy);	
			}
			XCopyArea(display, temp->data->cargo, offscreen, gc, 0, 0,
				systems->width, systems->height, temp->data->x+temp->data->x_offset,
				temp->data->y+temp->data->y_offset);
			XSetClipOrigin(display,
				temp->data->contexts[temp->data->index],
				net->computers[temp->data->target_c].x,
				net->computers[temp->data->target_c].y);
			XCopyArea(display,
				temp->data->cels[temp->data->index], offscreen,
				temp->data->contexts[temp->data->index],
				0, 0, bill->awidth, bill->aheight,
				net->computers[temp->data->target_c].x,
				net->computers[temp->data->target_c].y);
			temp->data->index++;
			temp=temp->next;
		}
	}
}

/* Updates bills who are fleeing with their ill gotten gains */
void update_out(horde *bill, network *net, library *systems, int level){
	node *temp = bill->out;
	int dx;
	if (empty(bill->out)) return;
	while (temp){
		if (INTERSECT(temp->data->x, temp->data->y, bill->width,
			bill->height, 0, 0, (int) width, (int) height))
		{
			move_bill(temp->data, bill, level, FAST);
			temp->data->index++;
			temp->data->index%=WALK_CELS;
			temp->data->y_offset+=(8*(temp->data->index%2)-4); 				
			XSetClipOrigin(display,
				temp->data->contexts[temp->data->index],
				temp->data->x,temp->data->y);
			if (temp->data->cargo)
				XCopyArea(display, temp->data->cargo, 
					offscreen, gc, 0, 0, systems->width,
					systems->height, temp->data->x+temp->data->x_offset,
					temp->data->y+temp->data->y_offset);
			XCopyArea(display, temp->data->cels[temp->data->index],
				offscreen, temp->data->contexts[temp->data->index],
				0, 0, bill->width, bill->height,
				temp->data->x, temp->data->y); 
			temp=temp->next;
		}
		else{
			node *travel = temp;
			temp = travel->next;
			delete(travel, &bill->out);
			bill->on_screen--;bill->off_screen++;
		}
	}
}


/* Updates bills who are dying */
void update_dying(horde *bill, network *net, library *systems, int level){
	node *temp = bill->dying;
	if (empty(bill->dying)) return;
	while (temp){
		if (temp->data->index < DEATH_CELS){
			XSetClipOrigin(display,temp->data->contexts[temp->data->index],
				temp->data->x,temp->data->y);
			if (temp->data->cargo) {
				temp->data->y_offset +=(temp->data->index*GRAVITY);
				XCopyArea(display, temp->data->cargo, offscreen,
					gc, 0, 0, systems->width,
					systems->height, temp->data->x+temp->data->x_offset,
					temp->data->y+temp->data->y_offset);
			}
			XCopyArea(display, temp->data->cels[temp->data->index], offscreen, 
				temp->data->contexts[temp->data->index], 0, 0, bill->width, 
				bill->height, temp->data->x, temp->data->y);
			temp->data->index++;	
			temp=temp->next;
		}
		else {
			node *travel = temp;
			temp = travel->next;
			travel->data->y+=travel->data->y_offset;
			if (!(travel->data->cargo) || travel->data->cargo == systems->os[WINGDOWS])
				delete(travel, &bill->dying);
			else move(travel, &bill->dying, &systems->strays);
			bill->on_screen--;
		}
	}
}

/*  updates dropped oses  */
void update_strays (library *systems) {
	node *temp=systems->strays;
	if (empty(systems->strays)) return;
	while (temp) {
		if (temp != systems->grabbed)
			XCopyArea(display, temp->data->cargo, offscreen, gc,
				0, 0, systems->width, systems->height,
				temp->data->x, temp->data->y);
		temp=temp->next;
	}
}

void update_info(Widget info, horde *bill, network *net, library *systems,
	int level) 
{
	static char str[80], oldstr[80];
	int temp = (100*net->base-10*net->nt)/net->units;
	sprintf (str, "Bill:%d/%d   Computer:%d/%d/%d   Level %d   Score %d",
		bill->on_screen, bill->off_screen, net->base, net->off,
		net->nt, level, score);
	if (strncmp(str, oldstr, 80)) {
		print_to_widget(info, str);
		strcpy (oldstr, str);
	}
	efficiency += temp;
}
  
void update_score (horde *bill, network *net, int level, int action) {
	switch (action){
		case ENDLEVEL: score+=(level*efficiency/iteration); break;
		case 1:  score+=BILLPOINTS; break;
		default: score+=(action*action*BILLPOINTS*BILLPOINTS);
	}
}

/*  setup level  */
void setup_level (horde *bill, network *net, library *systems, int lev) {
	destroy(&bill->in);
	destroy(&bill->at);
	destroy(&bill->out);
	destroy(&bill->dying);
	destroy(&systems->strays);
	level = lev;
	bill->off_screen =bills_on(level);
	bill->on_screen = 0;
	systems->grabbed=(node *)NULL;
	XDefineCursor(display, window, defaultcursor);
	net->units =computers_on(level);
	setup_network(net);
	net->base = net->units;
	net->off = net->nt = 0;
	iteration = 0;
	efficiency = 0;
}

void end_game (horde *bill, network *net, library *systems) {
	destroy(&bill->in);
	destroy(&bill->at);
	destroy(&bill->out);
	destroy(&bill->dying);
	destroy(&systems->strays);
}

void read_high_scores(scorelist *scores) {
	FILE *scorefile ;
	int i, j;
	scorefile = fopen (SCORE_FILE, "r");
	if (scorefile)
		for (i=0; i<10; i++) {
			for (j=0; j<20; j++)
				scores[i].name[j] = fgetc(scorefile);
			scores[i].name[20]=0;
			fscanf (scorefile, "%d%d\n", &(scores[i].level),
				&(scores[i].score));
		}
	else {
		for (i=0; i<10; i++) {
			strcpy(scores[i].name, "me");
			scores[i].level = scores[i].score = 0;
		}
	}
	fclose(scorefile);
}

void write_high_scores(scorelist *scores) {
	int i, j;
	FILE *scorefile = fopen (SCORE_FILE, "w");
	if (!scorefile) {
		fclose(scorefile);
		return;
	}
	for (i=0; i<10; i++) {
		for (j=0; j<strlen(scores[i].name); j++)
			fputc(scores[i].name[j], scorefile);
		for (j=strlen(scores[i].name); j<20; j++)
			fputc(' ', scorefile);
		fflush(scorefile);
		fprintf (scorefile, " %d %d\n", scores[i].level,
			scores[i].score);
	}
	fclose(scorefile);
}

/*  Warp to specified level   */
void warp_to_level (int lev) {
	XDefineCursor(display, window, defaultcursor);
	if (state==PLAYING) {
		if (lev <= level) return;
		level = lev;
		setup_level(&bill, &net, &systems, level);
	}
	else {
		if (lev<=0) return;
		new_game ((Widget)0, (XtPointer)NULL, (XtPointer)NULL);
		level = lev;
	}
}

/*  Add new high score to list   */
void recalc_high_scores (char *str) {
	int i, j;
	for (i=9; i>=0; i--)
		if (scores[i].score > score) break;
	if (i==9) return;
	for (j=8; j>i; j--) {
		strcpy (scores[j+1].name, scores[j].name);
		scores[j+1].level = scores[j].level;
		scores[j+1].score = scores[j].score;
	}
	i++;
	strcpy (scores[i].name, str);
	scores[i].level = level;
	scores[i].score = score;
}

/*  New game callback function*/
void new_game (Widget w, XtPointer client_data, XtPointer call_data) {
	state = PLAYING;
	XDefineCursor(display, window, defaultcursor);
	setup_level(&bill, &net, &systems, 1);
	score = 0;
	XtRemoveTimeOut(timer);
	timer = XtAppAddTimeOut (app, INTERVAL, update_all, NULL);
	XtSetSensitive(pausebutton, True);
}

/*  Quit game callback function*/
void quit_game (Widget w, XtPointer client_data, XtPointer call_data) {
	write_high_scores(scores);
	exit(0);
}

void popdown (Widget w, XtPointer client_data, XtPointer call_data) {
	in_popup=0;
	XtSetSensitive(menubar, True);
}

void popup (Widget w, Widget *box, XtPointer call_data) {
	XtManageChild(*box);
	XtAddCallback(XtParent(*box), XtNpopdownCallback, (XtCallbackProc) popdown, NULL);
	in_popup=1;
	XtSetSensitive(menubar, False);
	XtPopup(XtParent(*box), XtGrabNone);
	while (in_popup==1 || XtAppPending(app))
		XtAppProcessEvent(app, XtIMAll);
}

/*  Helper function for event handling of hits */
void process_hits (horde *bill, list *curlist, XButtonEvent *event, int *count) {
	node *temp=*curlist;
	while (temp) {
		if (INTERSECT(event->x, event->y, 0, 0, temp->data->x,
			temp->data->y, bill->width, bill->height))
		{
			node *travel = temp;
			temp = travel->next;
			travel->data->index = 0;
			travel->data->cels = bill->dcels;
			travel->data->contexts = bill->dcontexts;
			travel->data->y_offset = -15;
			travel->data->x_offset = -2;
			move(travel, curlist, &bill->dying);
			(*count)++;
		}
		else temp=temp->next;
	}

}

/*  Event handler function (button press) */
void hit_monster(Widget w, horde *bill, XButtonEvent *event) {
	int i = 0;
	node *temp;
	if (state != PLAYING) return;

	XDefineCursor(display, window, downcursor);
	process_hits(bill, &bill->in, event, &i);
	process_hits(bill, &bill->at, event, &i);
	process_hits(bill, &bill->out, event, &i);
	update_score(bill,&net,level,i);
	if (i) return;

	temp = systems.strays;
	while (temp) {
		if (INTERSECT(event->x, event->y, 0, 0, temp->data->x,
			temp->data->y, systems.width, systems.height))
		{
			for (i=1; i<=NUM_OS && 
				systems.os[i]!=temp->data->cargo; i++);
			XDefineCursor(display, window, systems.cursor[i]);
			systems.grabbed=temp;
			return;
		}
		else temp=temp->next;
	}		
}

/*  Event handler function (button release) */
void drop_os(Widget w, library *systems, XButtonEvent *event) {
	int i;
	if (state != PLAYING) return;
	if (!systems->grabbed) {
		XDefineCursor (display, window, defaultcursor);
		return;
	}
	for (i=0; i<net.units; i++)
		if (INTERSECT(event->x, event->y, systems->width,
			systems->height, net.computers[i].x+OS_OFFSET,
			net.computers[i].y+OS_OFFSET, systems->width,
			systems->height)
			&&
			systems->os[net.computers[i].os] == 
			systems->grabbed->data->cargo
			&&
			net.computers[i].status != BASE_STATE)
		{
			net.base++; 
			if (net.computers[i].status == WINGDOWS_STATE)
				net.nt--;
			else
				net.off--;
			net.computers[i].status=BASE_STATE;
			net.computers[i].picture = 
				net.pictures[net.computers[i].os][BASE_STATE];
			XDefineCursor(display, window, defaultcursor);
			delete (systems->grabbed, &systems->strays);
			systems->grabbed=(node *) NULL;
			return;
		}
	XDefineCursor(display, window, defaultcursor);
	systems->grabbed = (node *) NULL;
}	

/*  Event handler function (leave window) */
void leave_window(Widget w, XtPointer client_data, XEvent *event) {
	in_window = 0;
}

/*  Event handler function (enter window) */
void enter_window(Widget w, XtPointer client_data, XEvent *event) {
	in_window = 1;
	refresh_field(field);
}

/* Event handler (expose) */
void redraw_window(Widget w, XtPointer client_data, XEvent *event) {
	refresh_field(field);
}

/* Event handler (key hit - debugging) */
void print_debug_info(Widget w, XtPointer client_data, XKeyEvent *event) {
	node *temp;
	if (event->keycode != XKeysymToKeycode(display, 'd')) return;
	printf ("Bills going in:\n");
	for (temp=bill.in; temp; temp=temp->next) printf ("\t%d, %d\n", temp->data->x, temp->data->y);
	printf ("Bills at computers :\n");
	for (temp=bill.at; temp; temp=temp->next) printf ("\t%d, %d\n", temp->data->x, temp->data->y);
	printf ("Bills going out:\n");
	for (temp=bill.out; temp; temp=temp->next) printf ("\t%d, %d\n", temp->data->x, temp->data->y);
}

void update_all(XtPointer client_data, XtIntervalId *timer_id) {
	int n;
	timer = XtAppAddTimeOut (app, INTERVAL, update_all, NULL);
	if (in_window && !in_popup) {
		/* Game routines */
		clean_offscreen (values.foreground, values.background); 
		switch (state) {
		case PLAYING:
			update_network(&net);
			if (!(iteration % between_bills(level)))
				launch_bills(&bill, &net, &systems,
					max_bills_at_once(level)); 
			update_strays(&systems);
			update_at (&bill, &net, &systems, level);
			update_in (&bill, &net, &systems, level);
			update_out (&bill, &net, &systems, level);
			update_dying(&bill, &net, &systems, level);
			if (!(iteration%3)) update_info(info, &bill,
				&net, &systems, level);
			if (!(bill.on_screen+bill.off_screen)) {
				update_score(&bill, &net, level, ENDLEVEL);
				state = BETWEEN;
				n = 0;
			}
			if (!(net.base+net.off)) {
				end_game(&bill, &net, &systems);
				state = END;
				n = 0;
			}
			break;
		case END:
			XtRemoveTimeOut(timer);
			timer = XtAppAddTimeOut (app, INTERVAL*1000, NULL, NULL);
			XtSetSensitive(pausebutton, False);
			convert_to_toasters(&net);
			update_network(&net);
			refresh_field(field);
			popup(NULL, &endgamebox, NULL);
			if (score > scores[9].score) {
				popup(NULL, &enternamebox, NULL);
			}
			update_high_scores(highscorebox, scores);
			popup(NULL, &highscorebox, NULL);
			sleep(1);
			clean_offscreen (values.foreground, values.background); 
			XCopyArea(display, logo, offscreen, gc, 0, 0,
				LOGO_WIDTH, LOGO_HEIGHT, (width-LOGO_WIDTH)/2,
				(height-LOGO_HEIGHT)/2);
			break;
		case BETWEEN:
			update_scorebox(scorebox, level, score);
			popup (NULL, &scorebox, NULL);
			state = PLAYING;
			setup_level(&bill, &net, &systems, ++level);
			break;
		}
		if (!slow || iteration/2) refresh_field(field);
		iteration++;
	}
}

/***************************************MAIN**********************************/
main(int argc, char **argv) {
	int n;
	char c;
	Arg wargs[10];
	extern char *optarg;
	Pixmap p1, p2, p3, p4, p5;

	srand(time(NULL));

/****************************/
/*  Create toplevel widget  */
/****************************/
	toplevel = XtAppInitialize (&app, "XBill", NULL, 0, &argc, argv, NULL, NULL, 0);
	setup_main_widgets();
	XtRealizeWidget(toplevel);
	DPRINTF("Realized toplevel\n");

/****************************/
/*    Handle arguments      */
/****************************/
	level = 0;
	while ((c = getopt(argc, argv, "l:L:sv")) != -1)
		switch(c) {
			case 'l':
			case 'L': level = atoi(optarg);
				  if (level<1) level = 1;
				  break;
			case 's': slow=1;
				  break;
			case 'v': printf ("XBill version 1.0\n"); exit(0);
		}

/*****************************/
/* Assign non-widget globals */
/*****************************/
	display = XtDisplay(toplevel);
	screen = XtScreen(toplevel);
	rootwindow = RootWindowOfScreen(screen);
	mainwindow = XtWindow(toplevel);
	depth = DefaultDepthOfScreen(screen);
	colormap = DefaultColormap(display, DefaultScreen(display));
	n=0;
	XtSetArg (wargs[n], XtNcolormap, colormap); n++;
	XtSetArg (wargs[n], XtNdepth, depth); n++;
	XtSetValues(toplevel, wargs, n);
	XtSetValues(base, wargs, n);
	XtSetValues(menubar, wargs, n);
	XtSetValues(field, wargs, n);
	XtSetValues(info, wargs, n);

/*****************************/
/*    Set up game board      */
/*****************************/
	window = XtWindow(field);
	fix_window_size();
	DPRINTF("Hey, we created a window\n");
	
/*****************************/	
/*   Create graphics pane    */
/*****************************/
	n=0;
	XtSetArg (wargs[n], XtNwidth, &width);n++;
	XtSetArg (wargs[n], XtNheight, &height);n++;
	XtSetArg (wargs[n], XtNforeground, &values.foreground);n++;
	XtSetArg (wargs[n], XtNbackground, &values.background);n++;
	XtGetValues(field, wargs, n);
	gc = XCreateGC (display, rootwindow, GCForeground|GCBackground,
		&values);
	XSetGraphicsExposures(display, gc, FALSE);
	offscreen=XCreatePixmap(display, rootwindow, width, height, depth);
	DPRINTF("Hey, we created the offscreen pixmap\n");
	attr.valuemask = 0L;
	attr.valuemask |= XpmCloseness;
	attr.valuemask |= XpmReturnPixels;
	attr.valuemask |= XpmColormap;
	attr.valuemask |= XpmDepth;
	attr.closeness = 65535;
	attr.colormap = colormap;
	attr.depth = depth;
	DPRINTF("Hey, we set up the XPM stuff\n");

/************************************/	
/* Initialize Icon & Opening Screen */
/************************************/

	load_logo(&logo, attr);
	DPRINTF("Hey, we loaded the logo\n");
	clean_offscreen (values.foreground, values.background); 
	XCopyArea(display, logo, offscreen, gc, 0, 0, LOGO_WIDTH, 
		LOGO_HEIGHT, (width-LOGO_WIDTH)/2, (height-LOGO_HEIGHT)/2);
	refresh_field(field);
	load_icon(&icon, attr);
	XtVaSetValues (toplevel, XtNiconPixmap, icon, NULL);
	DPRINTF("Hey, we loaded the icon\n");

/**********************************/
/*       Set up dialog boxes      */
/**********************************/

	load_dialog_pixmaps(&about, &rules, &thanks, &story, &freeware, attr);
	setup_other_widgets(about, rules, thanks, story, freeware);
	DPRINTF("Hey, we set up the other widgets\n");
	read_high_scores(scores);
	update_high_scores(highscorebox, scores);
	DPRINTF("Hey, we got the high scores\n");
	
/*****************************/	
/*   Initialize Bill Horde   */
/*****************************/	
	init (&bill.in);
	init (&bill.at);
	init (&bill.out);
	init (&bill.dying);
	DPRINTF("Hey, we set up some linked lists\n");
	XSetForeground(display, gc, values.background);
	load_bill_pixmaps(&bill, attr);
	XSetForeground(display, gc, values.foreground);
	DPRINTF("Hey, we loaded the bill pictures\n");

/*****************************/	
/*   Initialize OS Array     */
/*****************************/		

	init (&systems.strays);
	load_systems_pixmaps_and_bitmaps(&systems, attr);
	systems.grabbed = (node *) NULL;
	DPRINTF("Hey, we loaded the OS pictures\n");
	
/***********************************/	
/*   Initialize Computer Network   */
/***********************************/

	load_net_pixmaps(&net, &systems, attr);
	DPRINTF("Hey, we loaded the computer pictures\n");

/***********************************/	
/*       Initialize Cursors        */
/***********************************/

	load_cursors(&defaultcursor, &downcursor);
	XDefineCursor(display, window, defaultcursor);
	DPRINTF("Hey, we loaded the cursors\n");

/***********************************/	
/*   Last minute stuff before game */
/***********************************/

	in_window = 1;
	if (level) {
		setup_level(&bill, &net, &systems, level);
		timer = XtAppAddTimeOut (app, INTERVAL, update_all, NULL);
		state = PLAYING;
	}
	else timer = XtAppAddTimeOut (app, INTERVAL*1000, NULL, NULL);
	XtSetSensitive(pausebutton, False);
	DPRINTF("Hey, we're about to start...\n");

/**********   Main Loop  ***********/

	XtAppMainLoop(app);
}
