/* 	Written by Miguel A. Gavidia aka juvinious
	Use at your own risk. I take no responsibilities for
	any unknown issues that arise out of it.
	Any feedback is welcome to me at juvinious(at)gmail.com
	or on a.cc :D
*/
#include "amgc2_wrapper.h"
#include <iostream>
#include <string>
#include <sstream>
#include <signal.h>

using namespace std;

#define WHITE		makecol(255,255,255)
#define BLACK		makecol(0,0,0)
#define BLUE		makecol(0,0,255)
#define GREEN		makecol(0,255,0)
#define RED		makecol(255,0,0)
#define	YELLOW		makecol(255,255,0)
#define TEAL		makecol(0,255,255)
#define PINK		makecol(255,0,255)
#define PURPLE		makecol(150,0,150)
#define GREY		makecol(150,150,150)
#define RANDOMCOLOR	makecol(rand()%255,rand()%255,rand()%255)


// Fix redundancy
bool checkOpt(const char *opt, const char *c1, const char *c2, int str1=0, int str2=0)
{
	if(str1 && str2)
	{
		if(strncmp(opt,c1,str1)==0 || strncmp(opt,c2,str2)==0)return true;
	}
	else
	{
		if(strcmp(opt,c1)==0 || strcmp(opt,c2)==0)return true;
	}
	return false;
}

// My little helper
int kbHelper[KEY_MAX];

// Timer
volatile int speed_counter=0;
void increment_speed_counter()
{
	speed_counter++;
}
END_OF_FUNCTION(increment_speed_counter)
		
		
		
/*
	Presents the user with an Exit Menu from the main program	
*/
bool exitMenu(BITMAP *bmp,bool &exitFlag)
{
	bool quit=false;
	int cursor=0;
	BITMAP *buff = create_bitmap(SCREEN_W,SCREEN_H);
	blit(bmp,buff,0,0,0,0,bmp->w,bmp->h);
	while(!quit)
	{
		if(key[KEY_UP] && !kbHelper[KEY_UP])
		{
			kbHelper[KEY_UP] = 1;
			if(cursor==0)cursor=1;
			else cursor=0;
		}
		else if(!key[KEY_UP] && kbHelper[KEY_UP])
		{
			kbHelper[KEY_UP] = 0;
		}
		if(key[KEY_DOWN] && !kbHelper[KEY_DOWN])
		{
			kbHelper[KEY_DOWN] = 1;
			if(cursor==0)cursor=1;
			else cursor=0;
		}
		else if(!key[KEY_DOWN] && kbHelper[KEY_DOWN])
		{
			kbHelper[KEY_DOWN] = 0;
		}
		if(key[KEY_RIGHT] && !kbHelper[KEY_RIGHT])
		{
			kbHelper[KEY_RIGHT] = 1;
			if(cursor==0)cursor=1;
			else cursor=0;
		}
		else if(!key[KEY_RIGHT] && kbHelper[KEY_RIGHT])
		{
			kbHelper[KEY_RIGHT] = 0;
		}
		if(key[KEY_LEFT] && !kbHelper[KEY_LEFT])
		{
			kbHelper[KEY_LEFT] = 1;
			if(cursor==0)cursor=1;
			else cursor=0;
		}
		else if(!key[KEY_LEFT] && kbHelper[KEY_LEFT])
		{
			kbHelper[KEY_LEFT] = 0;
		}
		if(key[KEY_ESC]&&!kbHelper[KEY_ESC])
		{
			kbHelper[KEY_ESC]=1;
			quit = true;
		}
		else if(!key[KEY_ESC]&&kbHelper[KEY_ESC])
		{
			kbHelper[KEY_ESC]=0;
		}
		if(key[KEY_ENTER]&&cursor)
		{
			quit = exitFlag = true;
		}
		else if(key[KEY_ENTER]&&!cursor)
		{
			quit = true;
			exitFlag = false;
		}
		
		rect(buff,220,180,440,240,WHITE);
		rectfill(buff,221,181,439,239,BLACK);
		textout_centre_ex(buff,font,"Do you want to quit?",320,185,WHITE,-1);
		
		if(cursor)
		{
			rect(buff,240,200,280,220,WHITE);
			rectfill(buff,241,201,279,219,BLACK);
			textout_centre_ex(buff,font,"NO",260,210,WHITE,-1);
			
			rect(buff,380,200,420,220,RED);
			rectfill(buff,381,201,419,219,GREEN);
			textout_centre_ex(buff,font,"YES",400,210,WHITE,-1);
			
		}
		else
		{
			rect(buff,240,200,280,220,RED);
			rectfill(buff,241,201,279,219,GREEN);
			textout_centre_ex(buff,font,"NO",260,210,WHITE,-1);
			
			rect(buff,380,200,420,220,WHITE);
			rectfill(buff,381,201,419,219,BLACK);
			textout_centre_ex(buff,font,"YES",400,210,WHITE,-1);
		}
		
		
		blit(buff,screen,0,0,0,0,buff->w,buff->h);
	}
	destroy_bitmap(buff);
	
	return exitFlag;
}

/*

	Configurations for a game, prompts if there is any..
	Else it returns false meaning there is none

*/

bool configureSettings(const char *game)
{
	IGame *newGame = loadPlugin(game);
	GameSettings *settings = newGame->GetSettings();
	string path(getFilePath(game));
	path += std::string(newGame->GetName());

	int settingsCount=0;
	
	if(settings)
	{
		while (settings[settingsCount].name) 
		{
			++settingsCount;
		}
		if(!loadSettings(settings,path.c_str()))
		{
			cout << "No previous configuration found! Needs configure\n" << path;
		}
		for (int i=0; i<settingsCount; i++) {
			int totalTokens = 0;
			if(!settings[i].options)
			{
						// True/False
				int choice = -1;
				bool done=false;
				while(!done)
				{
					cout << "Title: "<< settings[i].name << "\n";
					cout << "Default is "  << "[" << settings[i].value << "]\n";
					cout << "[1] Enable\n[2] Disable\n";
					cout << "Enter value: ";
					string tmpStr;
					getline (cin,tmpStr);
					stringstream(tmpStr) >> choice;
					if((choice==0) || (choice == 1))
					{
						settings[i].value = choice;
						done=true;
						break;
					}
				}
			}
			else
			{
						// Numbers
				totalTokens = count_tokens(settings[i].options);
				bool doWorded = false;
				for (int j=0; j<totalTokens; j++) 
				{
					const char *tok = get_token(settings[i].options, j);
	
					if (uatof(tok) != 0 || (tok && tok[i] == '0')) {
						continue;
					}
					else {
							// Worded options
						doWorded = true;
						break;
					}
				}
				if(!doWorded)
				{
					int choice = -1;
					while(choice!=-50)
					{
						cout << "Title: "<< settings[i].name << "\n";
						cout << "Default is "  << "[" << settings[i].value << "]\n";
						for (int j=0; j<totalTokens; j++)
						{
							cout << "[" << j << "] " << get_token(settings[i].options, j) << "\n";
						}
						cout << "Enter value: ";
						string tmpStr;
						getline (cin,tmpStr);
						stringstream(tmpStr) >> choice;
						if((choice>=0) && (choice<=totalTokens))
						{
							settings[i].value = choice;
							choice=-50;
							break;
						}
					}
				}
				else
				{
					int choice = -1;
					while(choice!=-50)
					{
						cout << "Title: "<< settings[i].name << "\n";
						cout << "Default is "  << "[" << settings[i].value << "]\n";
						for (int j=0; j<totalTokens; j++)
						{
							cout << "[" << j << "] " << get_token(settings[i].options, j) << "\n";
						}
						cout << "Enter value: ";
						string tmpStr;
						getline (cin,tmpStr);
						stringstream(tmpStr) >> choice;
						if((choice>=0) && (choice<=totalTokens))
						{
							settings[i].value = choice;
							choice=-50;
							break;
						}
					}
				}
			}
		}
		cout << "Saving Settings\n";
		saveSettings(settings,path.c_str());
		return true;
	}
	
	cout << "This game has no configuration.\n";
	
	return false;
}

/*

	Key Configuration

*/

void configureKeys()
{
	/*
	cout << "Entering Key Configuration\n";
	KeySettings tempKeys;
	if (newGame->RedefinesKeys()) {
	tempKeys.Read((gameName + ".cfg").c_str(), "keys");
}
	else {
	tempKeys.Read(AGC2_CONFIG_FILE, "GLOBAL_KEYS");
}
	int choice=-1;
	while(choice!=50)
	{
	for(int i=0;i<8;++i)
	{
	KeySettings::Key tmpKey = tempKeys.GetKey(i);
	cout << "[" << i << "] " << tmpKey.name << " (" << tmpKey.code << ")\n";
}
	cout << "[8] Finished\n";
	string tmpStr;
	getline (cin,tmpStr);
	stringstream(tmpStr) >> choice;
	if((choice>=0) && (choice<=7))
	{
	cout << "Enter new key for " << tempKeys.GetKey(choice).name << ": ";
	tempKeys.GetKey(choice).code = cin.get();
	cout << "\n";
	choice=-50;
	break;
}
	else if(choice==8)
	{
	choice=-50;
	break;
}
}
	if (newGame->RedefinesKeys()) {
	tempKeys.Write((gameName + ".cfg").c_str(), "keys");
}
	else {
	tempKeys.Write(AGC2_CONFIG_FILE, "GLOBAL_KEYS");
} 
	*/
}

/*
	
	Gets the settings for a plugin
	both GameSettings and KeySettings

*/

void grabSettings(const char *file, IGame *game, KeySettings &k)
{
	string name(getFilePath(file));
	name += std::string(game->GetName());
DEBUG("main.log")
	if(!loadSettings(game->GetSettings(),name.c_str()))
	{
		cout << "It looks like there is no settings for this game\n";
		cout << "You can try to configure it by passing -c when loading the game\n";
	}
	else cout << "Settings loaded correctly from " << name << ".cfg\n";
	if (game->RedefinesKeys())
	{
DEBUG("main.log")
		cout << "Redefining keys\n";
		k.Read((name + ".cfg").c_str(), "keys");
	}
}

IGame *loadGame(const char *filename)
{
	cout << "Loading game " << getFileName(filename) << "\n";
	IGame *game = loadPlugin(filename);
	
	if(!game)cout << "Failed to load " << getFileName(filename) << "\n";
	else cout << "Successfully loaded " << game->GetName() << "\n";
	return game;
}

int main(int argc, char **argv)
{
	bool fullscreen = false;
	
	// Startup allegro
	cout << "Initiating Allegro\n";
	allegro_init();
	
	switch(argc)
	{
		case 1:
			help:
				cout << "AMGC2 plugin loader 1.8\n";
#ifdef ALLEGRO_WINDOWS
			cout << "Usage: amgc2load game.dll [OPT]\n\n";
#else
			cout << "Usage: amgc2load plugin.so [OPT]\n\n";
#endif
			cout << "OPTIONS:\n";
			cout << "--help, -h                          Display this help\n";
			cout << "--info, -i                          Displays the plugin info\n";
			cout << "--configure, -c                     Configure the plugin\n";
			cout << "--keys, -k                          Key reassignment\n";
			cout << "--fullscreen, -f                    Play game at fullscreen\n";
			return 0;
		case 3:
		{
			if(checkOpt(argv[2],"--help","-h"))goto help;
			else if(checkOpt(argv[2],"--info","-i"))
			{
				IGame *temp = loadGame(argv[1]);
				if(!temp)return 0;
				cout << "Game:         " << temp->GetName() << "\n";
				cout << "Version:      " << temp->GetVersion() << "\n";
				cout << "Description:\n" << temp->GetDescription() << "\n";
				cout << "Author:       " << temp->GetAuthor() << "\n";
				cout << "Unloading Resources\nShutting down\n";
				return 0;
			}
			else if(checkOpt(argv[2],"--configure","-c"))
			{
				configureSettings(argv[1]);
				return 0;
			}
			else if(checkOpt(argv[2],"--keys","-k"))
			{
				cout << "Changing keys is currently disabled\nEdit the plugin's config (plugin.cfg) file and add a section like this:\n[keys]\nkey1 = 82\nkey2 = 83\nkey3 = 84\nkey4 = 85\nkey5 = 118\nkey6 = 75\nkey7 = 120\nkey8 = 116\nYou will need to change the number according to the appropriate scancode.\n";
				return 0;
			}
			else if(checkOpt(argv[2],"--fullscreen","-f"))fullscreen=true;
			else
			{
				cout << "Unknown option " << argv[2] << "! Displaying help\n";
				goto help;
			}
		}
		case 2:
		{
			if (checkOpt(argv[1], "-","--",1,2))goto help;
		}
	}
		
	//Create game variables and load configuration
DEBUG("main.log") 
	IGame *newGame = 0;
	KeySettings keys;
	int gameKeys[8];
	int mouse[4];
	BITMAP *buffer=NULL;
	bool quit=false;
	bool paused=false;
	
	// Allegro stuff
	install_timer();
	LOCK_VARIABLE(speed_counter);
	LOCK_FUNCTION(increment_speed_counter);
	install_int_ex(increment_speed_counter, BPS_TO_TIMER(60));
	
	install_keyboard();
	install_mouse();
	if (install_sound( DIGI_AUTODETECT, MIDI_AUTODETECT, NULL ) != 0)
	{
		cout << "Couldn't init sound. Please check your configuration\n";
		cout << "Disabling sound\n";
		install_sound( DIGI_NONE, MIDI_NONE, NULL);
	}
	
	// Setup screen
	set_color_depth(32);
	if(fullscreen)
	{
		cout << "Changing to fullscreen mode\n";
		if(set_gfx_mode(GFX_AUTODETECT_FULLSCREEN,640,480,0,0)!=0)
		{
			cout << "Unable to setup GFX mode.\n";
			goto end;
		}	
	}
	else
	{
		cout << "Opening in window\n";
		if(set_gfx_mode(GFX_AUTODETECT_WINDOWED,640,480,0,0)!=0)
		{
			cout << "Unable to setup GFX mode\n";
			goto end;
		}	
	}

DEBUG("main.log")
	// Load game and it's settings'
	cout << "Please wait while game initiates...\n";
DEBUG("main.log")
	newGame = loadGame(argv[1]);
	if(!newGame)
	{
		cout << "\nOops! There was a problem with the plugin\nTerminating\n";
		goto end;
	}
DEBUG("main.log")	
	grabSettings(argv[1], newGame, keys);
	
	for (int i=0; i<8; i++) {
		gameKeys[i] = key[keys.GetKey(i).code];
	}
	mouse[0] = mouse_x;
	mouse[1] = mouse_y;
	mouse[2] = mouse_b;
	mouse[3] = mouse_z;
DEBUG("main.log")
	if(!newGame->Init())
	{
		cout << "\nThere were problems setting up the plugin\nAttempting to shutdown\n";
		goto end;
	}
	
	cout << "Allocating screen buffer\n";
	buffer = create_bitmap(640,480);
	cout << "Starting game\n";
	// Game loop
	while(!quit)
	{
		if(paused)
		{
			if(exitMenu(buffer,quit))break;
			else
			{
				paused=false;
				newGame->Resume();
			}
		}
		else
		{
			bool draw = false;
			while(speed_counter) {
				draw = true;
				//Process key for exiting out of process
				if(key[KEY_ESC]&&!kbHelper[KEY_ESC])
				{
					speed_counter=0;
					rest(1);
					kbHelper[KEY_ESC]=1;
					paused=true;
					newGame->Pause();
					break;
				}
				else if(!key[KEY_ESC]&&kbHelper[KEY_ESC])
				{
					kbHelper[KEY_ESC]=0;
				}
				if((key[KEY_ALT]&&key[KEY_Q])&&(!kbHelper[KEY_ALT]&&!kbHelper[KEY_Q]))
				{
					cout << "Quick exit!\n";
					quit=true;
				}
			
				//Set the key states
				for(int i=0;i<8;++i)
				{
					if(key[keys.GetKey(i).code])gameKeys[i]=1;
					else gameKeys[i]=0;
				}
				//Set mouse states
				mouse[0] = mouse_x;
				mouse[1] = mouse_y;
				mouse[2] = mouse_b;
				mouse[3] = mouse_z;
				
				float dT = 1000.0f / 60.0f;
					
				if(newGame->Logic(gameKeys,mouse,dT)!=0)
				{
					quit=true;
					break;
				}
				
				speed_counter--;
			}
			
			if ( draw ){
				newGame->Draw(buffer);
				
				if(newGame->UseSystemMouse())show_mouse(buffer);
				blit(buffer,screen,0,0,0,0,SCREEN_W,SCREEN_H);
			}

			while(!speed_counter){
				rest( 1 );
			}
		}
	}
	
	// Print the score to the user
	if(newGame->Score()!=-1)cout << "Your score was: " << newGame->Score() << "\n";

end:
DEBUG("main.log")
	cout << "Unloading Resources\nShutting down\n";
	
	//delete newGame;
DEBUG("main.log")
	if(buffer!=NULL)destroy_bitmap(buffer);
	return 0;
}
END_OF_MAIN()
