/*********************************************************************//**
*	Commands library.
*	Knihovna obsahuje vsechny prikazy a k nim prirazene
*			funkce a parametry. Pri nejake akci je prikaz vytazen
*			podle klice a zavola se prislusna funkce s parametrem.
*
*	author: Michal Jirous
*	date: 29.7.2008
*	file: ctrl_commands.cpp
**********************************************************************/


#include "ctrl_commands.h"
#include "ctrl_gamecontrol.h"
#include "sys_console.h"
#include "sys_controller.h"
#include "ctrl_com_definition.h"
#include <map>
#include "ctrl_weapon_manager.h"

using namespace systemConsole;
using namespace std;
using namespace commandsLibrary;
using namespace comdef;

map<string, command> g_mapCommands;


int REGULAR = 1;
int REVERSE = 0;

command::command( std::string sCommand, void (*fnc)(void*), bool parameter, bool systemAllowed)
{
	this->sCommand = sCommand;
	function = fnc;
	reverse = NULL;
	par = parameter;
	isAllowed = systemAllowed;
}

command::command()
{
	function = NULL;
	reverse = NULL;
	par = false;
	isAllowed = SYS_NOT_ALLOWED;
}


void commandsLibrary::init()
{
	console << "Initializing game variables...";
	initCommands();
	console << "done" << endline;
}

void commandsLibrary::initCommands()
{
	//vytvoreni prikazu
	commandsLibrary::addCom( FORWARD_NAME, &gameControl::goForward );
	commandsLibrary::addCom( FORWARD_REVERSE_NAME, &gameControl::goForward, FORWARD_NAME );
	commandsLibrary::addCom( BACKWARD_NAME, &gameControl::goBackward);
	commandsLibrary::addCom( BACKWARD_REVERSE_NAME, &gameControl::goBackward, BACKWARD_NAME);
	commandsLibrary::addCom( STEPLEFT_NAME, &gameControl::goLeft);
	commandsLibrary::addCom( STEPLEFT_REVERSE_NAME, &gameControl::goLeft, STEPLEFT_NAME);
	commandsLibrary::addCom( STEPRIGHT_NAME, &gameControl::goRight);
	commandsLibrary::addCom( STEPRIGHT_REVERSE_NAME,&gameControl::goRight, STEPRIGHT_NAME );
	commandsLibrary::addCom( JUMP_NAME, &gameControl::goJump);
	commandsLibrary::addCom( JUMP_REVERSE_NAME,&gameControl::goJump, JUMP_NAME );
	commandsLibrary::addCom( DUCK_NAME, &gameControl::goDuck);
	commandsLibrary::addCom( DUCK_REVERSE_NAME, &gameControl::goDuck, DUCK_NAME);

	commandsLibrary::addCom( MAIN_MENU_NAME, &gameControl::mainMenu);
	commandsLibrary::addCom( RESUME_NAME, &gameControl::resume);

	
	commandsLibrary::addCom( EXIT_NAME,&gameControl::quit, "", EXIT_PARAMETER, EXIT_SYS_ALLOWED);

	commandsLibrary::addCom( SHOWCONSOLE_NAME, &gameControl::showConsole, "", NO_PARAMETER, SYS_ALLOWED );
	commandsLibrary::addCom( MAP_NAME, &gameControl::loadMap, "", MAP_PARAMETER);
	commandsLibrary::addCom(BIND_NAME, &gameControl::bind,"", BIND_PARAMETER, BIND_SYS_ALLOWED);
	commandsLibrary::addCom( USE_NAME, &gameControl::use);
	commandsLibrary::addCom(USE_REVERSE_NAME, &gameControl::use, USE_NAME);
	commandsLibrary::addCom( ALIAS_NAME,&gameControl::alias,"", ALIAS_PARAMETER, ALIAS_SYS_ALLOWED);
	
	
	commandsLibrary::addCom( SAVECONFIG_NAME,&gameControl::saveconf,"", SAVECONFIG_PARAMETER, SAVECONFIG_SYS_ALLOWED);
	commandsLibrary::addCom( HUDSET_NAME, &gameControl::hudset, "", NO_PARAMETER, SYS_ALLOWED );
	commandsLibrary::addCom( HUDREFRESH_NAME, &gameControl::hudrefresh, "", NO_PARAMETER, HUDREFRESH_SYS_ALLOWED );
	
	commandsLibrary::addCom( ATTACK_NAME, &weapon_manager::attack);
	commandsLibrary::addCom( ATTACK_REVERSE_NAME, &weapon_manager::attack, ATTACK_NAME);
	
	commandsLibrary::addCom( ATTACK2_NAME, &weapon_manager::attack2);
	commandsLibrary::addCom( ATTACK2_REVERSE_NAME, &weapon_manager::attack2, ATTACK2_NAME);

	commandsLibrary::addCom( RELOAD_NAME, &weapon_manager::reload);
	commandsLibrary::addCom( RELOAD_REVERSE_NAME, &weapon_manager::reload, RELOAD_NAME);

	commandsLibrary::addCom( NEXT_WEAPON_NAME, &weapon_manager::nextweapon);
	commandsLibrary::addCom( PREV_WEAPON_NAME, &weapon_manager::prevweapon);
	commandsLibrary::addCom( SETWEAPON_NAME, &weapon_manager::setweapon,"", SETWEAPON_PARAMETER);

	commandsLibrary::addCom( RESTART_NAME, &gameControl::restart);
	commandsLibrary::addCom( BACK_IN_TIME_NAME, &gameControl::backintime);

	commandsLibrary::addCom( TIMESTOP_NAME, &gameControl::timestop);
	commandsLibrary::addCom( TIMESTOP_REVERSE_NAME, &gameControl::timestop, TIMESTOP_NAME);

	commandsLibrary::addCom( FORWARD_TELEPORT_NAME, &gameControl::forward_teleport);
	commandsLibrary::addCom( FORWARD_TELEPORT_REVERSE_NAME, &gameControl::forward_teleport, FORWARD_TELEPORT_NAME);
	
}

/* Provedeni prikazu */
bool commandsLibrary::doCommand( string sCommand, void* parameter)
{
	return doCommand( getCommand( sCommand ), parameter );
}

/* Provedeni prikazu */
bool commandsLibrary::doCommand( command *pCommand, void * parameter)
{
	if( !pCommand )
	{
		console << "Error: System error: 'doCommand' -> NULL pointer" << endline;
		return false;
	}

	if( !pCommand->isAllowed && (systemController::getRunningLevel() == systemController::LOADGAME || 
		systemController::getRunningLevel() == systemController::LOADMAP) )
	{
		console << "Error: Commands cannot be executed before game initialization." << endline;
		return false;
	}


	if( pCommand->function == NULL )
	{
		console << "Error: no function to execute" << endline;
		return false;
	}

	if( !pCommand->sAliasData.empty() )
		( pCommand->function)( &pCommand->sAliasData );
	else if( !pCommand->par )	//pokud prikaz nema parametry, nastavujeme hodnotu parametru podle toho, jestli je prikaz reverzni
	{
		if( pCommand->reverse == NULL )
			(pCommand->function)( &REVERSE );
		else
			(pCommand->function)( &REGULAR );
	}
	else
		(pCommand->function)( parameter );	//provedeni prikazu
	return true;
}

/* Pridani noveho prikazu */
void commandsLibrary::addCom( string sCommand, void (*func)(void*), string reverse, bool parameter, bool systemAllowed)
{
	command *reverseCommand = getCommand(reverse);	//vyhledani reverzniho prikazu

	command newCommand( sCommand, func );
	
	if( parameter )
		newCommand.par = parameter;
	newCommand.isDefault = true;
	newCommand.isAllowed = systemAllowed;

	g_mapCommands.insert( make_pair( sCommand, newCommand ) );
	if(reverseCommand == NULL)
		return;
	if( reverseCommand->function != NULL && reverseCommand->reverse == NULL )	//pokud je reverzni, tak dostane odkaz na reverzni prvek
	{
		
		reverseCommand->reverse = getCommand( sCommand );
	}
}


/* Vytvoreni noveoh prikazu typu alias */
void commandsLibrary::addAliasCom( string sCommand, void(*func)(void*), string reverse, bool parameter, string aliasData)
{
	command *reverseCommand = getCommand( sCommand );	//vyhledani zadaneho jmena v databazi
	if( reverseCommand != NULL && reverseCommand->isDefault )
	{
		console << "Error: Cannot overwrite default command name" << endline;
		return;	//pokud tento prikaz uz existuje a je to standardni prikaz, nelze ho prepsat
	}

	reverseCommand = getCommand(reverse);	//vyhledani reverzniho prvku

	command newCommand( sCommand, func );
	if( parameter )
		newCommand.par = parameter;
	newCommand.isDefault = false;
	newCommand.isAllowed = SYS_NOT_ALLOWED;

	if( !aliasData.empty() )	//seznam prikazu k provedeni
		newCommand.sAliasData = aliasData;
	
	g_mapCommands.insert( make_pair( sCommand, newCommand ) );

	if(reverseCommand == NULL)
		return;
	if(reverseCommand->function != NULL && reverseCommand->reverse == NULL)	//pokud existuje reverzni prikaz
	{
		reverseCommand->reverse = getCommand( sCommand );	//nastavime ukazatel na rev. prikaz
	}
}

/* Vraci prikaz podle jeho jmena */
map<std::string,command>::iterator *commandsLibrary::getCommandIter( string com )
{

	map<string,command>::iterator *iter = &g_mapCommands.find( com );
	if( (*iter) == g_mapCommands.end() )
		return NULL;
	return &(*iter);
}

command *commandsLibrary::getCommand( std::string sCommand )
{
	map<string,command>::iterator iter =  g_mapCommands.find( sCommand );
	if( iter == g_mapCommands.end() )
		return NULL;
	return &iter->second;
}
