/***************************************************************************
 *            ui.c
 *
 *  Thu Jan 27 23:08:25 2005
 *  Copyright  2005  Peter Baldwin
 *  pgbaldwi@uwaterloo.ca
 ****************************************************************************/

//implements
#include "events.h"

//uses
#include "graphics.h"
#include "world.h"
#include "console.h"


#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <string.h>



typedef struct {
	double x,y,w,h;
	char *text;
	bool visible;
	void (*action) (); //function pointer
} uiButton;

void uiNewGame();
void uiTutorial();
void uiRetryLevel();
void uiNextLevel();
void uiStartLevel();
void uiMainMenu ();
void uiQuit ();

void uiEndLevel(bool success);

#define MAX_BUTTONS 16
uiButton buttons[MAX_BUTTONS];
int num_buttons = 0;

uiButton *buttonNewGame, *buttonStartLevel,
	*buttonRetryLevel,*buttonNextLevel,
	*buttonMainMenu, *buttonTutorial,
	*buttonQuit;



double speed = 1;
const char *ON = "on";
const char *OFF = "off";
const double MAGNET_TIME = 2.0; //2 second
double fps_sample = 0;
double mouseX, mouseY;

int fps_count = 0;
int fps = 0;

bool uiToggleTesting = FALSE;
bool uiTogglePause = FALSE;

bool started, demo;
level active_level;
char *banner = "";

uiButton *uiAddButton(char *text, void (*action) ()) {
	if (num_buttons == MAX_BUTTONS) {
		cWarning("Maximum Buttons Reached");
		return NULL;
	}
	int index = num_buttons++;
	double w,h;
	gSetFontMedium();
	gGetTextDimensions(text,&w,&h);
	const double x = (wAspectRatio - w)/2;
	const double y = (1 - h)/2;
	uiButton newButton = { x,y,w,h,text,FALSE, action };
	buttons[index] = newButton;
	return &buttons[index];
}

bool uiButtonContains(uiButton *button, double X, double Y) {
	const double x = button->x;
	const double y = button->y;
	const double w = button->w;
	const double h = button->h;
	return X > x && X < x+w && Y > y && Y < y+h ;
}
void uiPaintButton(uiButton *button) {
	const double x = button->x;
	const double y = button->y;
	//const double w = button->w;
	const double h = button->h;
	gSetFontMedium();
	if (uiButtonContains(button,mouseX,mouseY)) {
		//highlight
		gSetColorGray();
		gDrawString(button->text,x-0.0075,y+h-0.0075);
	}
	gSetColorBlack();
	gDrawString(button->text,x,y+h);
}

void uiPaintBlock(wBlock *block) {
	double x = block->o.pos.x;
	double y = block->o.pos.y;
	double w = block->w;
	double h = block->h;
	gSetColorGray();
	if (block->soft) {
		gFillRect(x-w/2,y-h/2,w,h);
	} else {
		gDrawOutlinedRect(x-w/2,y-h/2,w,h);
	}
}
void uiPaintGnimmel(wGnimmel *gnimmel) {
	double x = gnimmel->o.pos.x;
	double y = gnimmel->o.pos.y;
	double s = gnimmel->size;
	if (gnimmel->frozen) {
		//frozen gnimmel (square)
		//highlight
		const double factor = 1.15;
		if (!demo && vDistance(vNew(mouseX,mouseY),gnimmel->o.pos) < s/2) {
			gSetColorGray();
			gFillRect(x-s/2*factor,y-s/2*factor,s*factor,s*factor);
		}
		
		gSetColorWhite();
		gDrawOutlinedRect(x-s/2,y-s/2,s,s);
	} else {
		//rolling gnimmel (circle)
		//highlight
		const double factor = 1.15;
		if (!demo && vDistance(vNew(mouseX,mouseY),gnimmel->o.pos) < s/2) {
			gSetColorGray();
			gFillArc(x-s/2*factor,y-s/2*factor,s*factor,s*factor,0,360);
		}
		
		if (gnimmel->magnet && wToggleExtension) {
			gSetColorGray();
		} else {
			gSetColorWhite();
		}
		gDrawOutlinedArc(x-s/2,y-s/2,s,s,0,360);
		
		
		/* causes more confusion than it saves
		if (gnimmel->dig) {
			char *dig = "\\/";
			double w,h;
			gSetColorBlack();
			gSetFontSmall();
			gGetTextDimensions(dig,&w,&h);
			gDrawString(dig,x-w/2,y+h/2);
		} else */ if (gnimmel->magnet && wToggleExtension) {
			char *label = "+";
			gSetFontSmall();
			double w,h;
			gGetTextDimensions(label,&w,&h);
			gSetColorBlack();
			gDrawString(label,x-w/3,y+h/3);
		}
		
	}
	if (uiToggleTesting) {
		if (wIsAirborne(gnimmel)) {
			gSetColorBlack();
			gSetFontSmall();
			gDrawString("A",x,y);
		}
	}
}
void uiPaintSound(wSound *sound) {
	double x = sound->pos.x;
	double y = sound->pos.y;
	gSetColorBlack();
	gSetFontSmall();
	double w,h;
	gGetTextDimensions(sound->subtitle,&w,&h);
	if (x < 0) { x = 0.01; }
	if (x > wAspectRatio) { x = wAspectRatio - w  - 0.01; }
	if (y < 0) { y = h + 0.01; }
	if (y > 1) { y = 1 - 0.01; }
	gDrawString(sound->subtitle,x,y);
	
}

void uiPaintEntrance(wEntrance *entrance) {
	double x = entrance->pos.x;
	double y = entrance->pos.y;
	double w = 0.125;
	double h = w;
	
	//TODO draw number of gnimmels waiting
	
	gSetColorWhite();
	gDrawOutlinedArc(x-w/2,y-h-w/2,w,w,0,360);
	gDrawOutlinedRect(x-w/2,y-h,w,h);
}

void uiPaintExit(wExit *exit) {
	double x = exit->pos.x;
	double y = exit->pos.y;
	double w = 0.125;
	double h = w*1.5;
	
	gSetColorWhite();
	gDrawOutlinedRect(x-w/2,y-h,w,h);
}

void uiPaintMagnet(wMagnet *magnet) {
	double d = magnet->size;
	double x = magnet->pos.x-d/2;
	double y = magnet->pos.y-d/2;
	
	gSetColorBlack();
	if (wToggleExtension) {
		gFillArc(x,y,d,d,0,360);
		char *label = (magnet->pole == NORTH) ? "+" : "-";
		gSetFontMedium();
		double w,h;
		gGetTextDimensions(label,&w,&h);
		gSetColorWhite();
		gDrawString(label,magnet->pos.x-w/3,magnet->pos.y+h/3);
	} else {
		gDrawArc(x,y,d,d,0,360);
	}
}

void uiPaintWorld() {
	int i, count;
	int total;
	int released = 0;
	int not_released = 0;
	int dead = 0;
	int frozen = 0;
	int inprogress = 0; //may finish
	int exited = 0;
	
	
	wEntrance *entrances = wGetEntrances(&count);
	for (i=0; i<count; i++) {	
		uiPaintEntrance(&entrances[i]);
		not_released += entrances[i].n;
	}	
	
	wExit *exits = wGetExits(&count);
	for (i=0; i<count; i++) {	
		uiPaintExit(&exits[i]);
		exited += exits[i].n;
	}	
	
	
	wMagnet *magnets = wGetMagnets(&count);
	for (i=0; i<count; i++) {	
		uiPaintMagnet(&magnets[i]);
	}
	
	
	wBlock *blocks = wGetBlocks(&count);
	for (i=0; i<count; i++) {	
		uiPaintBlock(&blocks[i]);
	}	
	wGnimmel *gnimmels = wGetGnimmels(&count);
	released += count;
	for (i=0; i<count; i++) {
		if (wIsActive(&gnimmels[i])) {
			uiPaintGnimmel(&gnimmels[i]);
			inprogress++;
		} else if (!gnimmels[i].alive) {
			dead++;
		}
			
	}
	
	if (wToggleExtension) {
		wSound *sounds = wGetSounds(&count);
		for (i=0; i<count; i++) {
			if (sounds[i].t>0) {
				uiPaintSound(&sounds[i]);
			}	
		}
	}
	total = released + not_released;
	
	//Note: this isn't really the correct place to handle this
	//however, it is the only spot in the UI code where every
	//element in the world is iterated over so this spot
	//is convienent
	if (!demo && active_level!=TEST_LEVEL && (total-dead < wGetGoal() || exited >= wGetGoal())) {
		uiEndLevel(exited>=wGetGoal());
	}
	
	//interface
	char buffer[1024];
	
	
	if (!demo) { //hide panel for demo
		gSetColorBlack();
		gSetFontSmall();
		sprintf(&buffer[0], "Released: %i/%i   Goal: %i/%i   Frozen: %i   Dead: %i",released,total,exited,wGetGoal(),frozen,dead);
		gDrawString(&buffer[0],1.1,0.95);
	}
}

void uiPaintInterface() {
	int i;
	char buffer[256];
	gSetColorBlack();
	gSetFontSmall();
	if (uiToggleTesting) {
		//fps
		//copy fps to buffer
		sprintf(&buffer[0],"FPS=%i",fps);	
		gDrawString(&buffer[0], 0.05,  0.05); //paint
		
		//mouse position
		sprintf(&buffer[0],"(x,y)=(%.2f,%.2f)",mouseX,mouseY);
		gDrawString(&buffer[0],0.4,0.05);
		
		//speed
		sprintf(&buffer[0],"Game speed=%.2f",speed);
		gDrawString(&buffer[0],1.5,0.05);	
	}
	//help
	/*
	gDrawRect(0.05,0.9,0.5,0.075);
	gDrawRect(0.05,0.9,0.5,0.15);
	gDrawRect( 0.2,0.9,0.15,0.15);
	gDrawString("Charge",0.1,0.95);
	gDrawString("Block",0.275,0.95);
	gDrawString("Dig",0.4,0.95);
	*/
	if (!demo) {
		gDrawString("Buttons:    L-Charge   M-Freeze    R-Dig",0.1,0.95);
	}
	
	//buttons
	for (i=0; i<num_buttons; i++) {
		if (buttons[i].visible) {
			uiPaintButton(&buttons[i]);
		}
	}
	char *text = ""; //default: draw nothing
	//title
	if (strlen(banner)>0) {
		text = banner;
	} else if (uiTogglePause) {
		text = "Paused";
	}
	
	if (strlen(text)) {
		//found something to draw
		double x,y,w,h;
		gSetFontLarge();
		gGetTextDimensions(text,&w,&h);
		x = (wAspectRatio - w)/2;
		y = (1 - h)/3+h;
		gSetColorBlack();
		gDrawString(text,x,y);
	}
}
void uiPaint() {
	gClearBuffer();
	uiPaintWorld();
	uiPaintInterface();
}

void uiInitLevel(level lvl) {
	int i;
	//clear buttons
	for (i=0; i<num_buttons; i++) {
		buttons[i].visible = FALSE;
	}	
	
	uiTogglePause = FALSE; //unpause when new level starts
	wInit( lvl );
	banner = wGetLevelTitle(); //get level title
	switch (lvl) {
		case TITLE_LEVEL:
			started = TRUE;
			demo = TRUE;
			buttonNewGame->visible = TRUE;
			buttonTutorial->visible = TRUE;
			buttonQuit->visible = TRUE;
			break;
		case TUTORIAL1:
		case TUTORIAL2:
		case TUTORIAL3:
		case LEVEL1:
		case LEVEL2:
		case LEVEL3:
		case LEVEL4:
		case TEST_LEVEL:
			started = FALSE;
			demo = FALSE;
			buttonStartLevel->visible = TRUE;
			break;
	}	
	
	active_level = lvl;
}

void uiInit( int argc, char *argv[] ) {
	
	
	
	gInit( argc, argv, wAspectRatio );
	
	//keep pointer to buttons
	buttonNewGame = uiAddButton("New Game", &uiNewGame);
	buttonTutorial =  uiAddButton("Tutorial", &uiTutorial);
	buttonTutorial->y = buttonNewGame->y + buttonNewGame->h*1.25;
	buttonQuit = uiAddButton("Quit", &uiQuit);
	buttonQuit->y = buttonTutorial->y+buttonTutorial->h*1.25;
	buttonRetryLevel = uiAddButton("Retry Level", &uiRetryLevel);
	buttonNextLevel = uiAddButton("Next Level", &uiNextLevel);
	buttonStartLevel = uiAddButton("Start Level", &uiStartLevel);
	buttonMainMenu =  uiAddButton("Main Menu", &uiMainMenu);
	
	uiInitLevel(TITLE_LEVEL);
	
}

void uiEndLevel(bool success) {
	if (success) {
		if (active_level == LEVEL3) {
			banner = "Game Complete!";
			buttonMainMenu->visible = TRUE;
		} else if (active_level == TUTORIAL3) {
			banner = "Tutorial Complete!";
			buttonMainMenu->visible = TRUE;
		} else {
			banner = "Level Complete!";
			buttonNextLevel->visible = TRUE;
		}
	} else {
		banner = "Level Failed!";
		buttonRetryLevel->visible = TRUE;
	}
}




void uiTick( double t) {

	//calc fps
	if (fps_sample>1) {
		fps = fps_count;
		fps_sample-=1; //reset
		fps_count = 0;
	} else {
		fps_count++; //increment
		fps_sample+=t;
	}
	
	gEvents();
	
	if (started && !uiTogglePause) {
		wTick(t * speed); //after events so elements don't move before the click is processed
	}
	
	uiPaint();
	gSwap();
}

void eMousePressed( double x, double y, mouse_button button )
{
	vector cursor = vNew(x,y);
	int i, count;
	wGnimmel *gnimmels;
	
	//button prompt before all interaction
	for (i=0; i<num_buttons; i++) {
		if (buttons[i].visible) {
			if (uiButtonContains(&buttons[i],x,y)) {
				//perform action
				(*buttons[i].action)();
				return;
			}
		}
	}
		
	if (started && !demo) {
		gnimmels = wGetGnimmels(&count);
		for (i=0; i<count; i++) {
			if (wIsActive(&gnimmels[i])) {
				if (vDistance(gnimmels[i].o.pos,cursor) < gnimmels[i].size/2) {
					if (button==BUTTON1) {
						if (wToggleExtension) {
							if (!gnimmels[i].frozen && !gnimmels[i].magnet) { //can't click repeatedly
								gnimmels[i].magnet = MAGNET_TIME;
							}
						}
					} else if (button==BUTTON2) {
						if (!gnimmels[i].frozen) {
							wFreezeGnimmel(&gnimmels[i]);
						} else {
							wDefrostGnimmel(&gnimmels[i]);
						}
					} else { //button==BUTTON3
						if (!gnimmels[i].frozen) {
							gnimmels[i].dig = TRUE;
						}
					}
					
				}
			}
		}
	}
}
void eMouseReleased( double x, double y, mouse_button button ) {}
void eKeyPressed( char c ) {
	const char ch = tolower(c);
	switch (ch) {
		case '0': //test level
		case '1':
		case '2':
		case '3':
			uiInitLevel(TEST_LEVEL+c-'0');
			break;
		case '4':
		case '5':
		case '6':
			uiInitLevel(TUTORIAL1+c-'4');
			break;
		case '':
			uiInitLevel(TITLE_LEVEL);
			break;
		case 'r': //restart level
			uiInitLevel(active_level);
			break;
		case 'f':
			wToggleRoll = wToggleRoll ? FALSE : TRUE;
			printf("Gnimmel roll force: %s\n", wToggleRoll ? ON : OFF );
			break;
		case 's':
			uiToggleTesting = uiToggleTesting ? FALSE : TRUE;
			if (!uiToggleTesting) speed = 1; //reset speed
			printf("Testing mode: %s\n", uiToggleTesting ? ON : OFF );
			break;
		case 'p':
			if (started && !demo) {
				uiTogglePause = uiTogglePause ? FALSE : TRUE;
				printf("Pause: %s\n", uiTogglePause ? ON : OFF );
			}
			break;
		case 't':
			wToggleExtension = wToggleExtension ? FALSE : TRUE;
			speed = 1; //restore speed
			printf("Extension: %s\n", wToggleExtension ? ON : OFF );
			break;
		case ' ':
			if (uiToggleTesting && started && !demo) {
				wReleaseGnimmel(mouseX,mouseY,1);
			}
			break;
		case '-':
			if (uiToggleTesting && speed>0) {
				speed-=0.25;
			}
			break;
		case '+':
		case '=': //works without SHIFT
			if (uiToggleTesting && speed < 16) {
				speed+=0.25;
			}
			break;
		
		default:
			printf("Unknown command: %c\n", ch);
	}
}
void eMouseMoved   ( double x, double y ) {
	mouseX = x;
	mouseY = y;
}

void uiNewGame() {
	uiInitLevel(LEVEL1);
}
void uiRetryLevel() {
	uiInitLevel(active_level);
}
void uiNextLevel() {
	uiInitLevel(active_level+1);
}
void uiStartLevel(){
	started = TRUE;
	banner = ""; //clear title
	buttonStartLevel->visible = FALSE;
}
void uiMainMenu () {
	uiInitLevel(TITLE_LEVEL);
}

void uiTutorial () {
	uiInitLevel(TUTORIAL1);
}

void uiQuit () {
	cQuit();
}
