/**************************************
*	commands.cpp
*	Autor: Michal Jirous
*	Popis: Trida pro praci s prikazy do konzole
*			
*
***************************************/

#include "commands.h"

command *fieldCom[HASH_COM_HEIGHT];


command::command(string c, void (CControl::*fnc)(string), bool parameter)
{
	com = c;
	x = fnc;
	reverse = NULL;
	par = parameter;
}


command::command()
{
	com = string("Unknown");
	next = NULL;
	x = NULL;
	reverse = NULL;
	par = false;
}

void commandsLibrary::init()
{
	for(int i=0;i<HASH_COM_HEIGHT;i++)
		fieldCom[i] = NULL;
}

/* Provedeni prikazu */
bool commandsLibrary::doCommand(CControl *c, string s, string par)
{
	command *com = getCommand(s);	//vyhledani prikazu v databazi
	if(com == NULL)
		return false;
	
	if(com->x == NULL)
		return false;
	string value;
	if(!com->par)	//pokud prikaz nema parametry, nastavujeme hodnotu parametru podle toho, jestli je prikaz reverzni
		(com->reverse == NULL) ? value = string("1") : value = string("0");
	else
		value = par;
	(c->*(com->x))(value);	//provedeni prikazu
	return true;
}

/* Provedeni prikazu */
bool commandsLibrary::doCommand(CControl *c,command *cmd,string par)
{
	command *com = cmd;
	if(com == NULL)
		return false;
	
	if(com->x == NULL)
		return false;
	string value;
	if(!com->sAliasData.empty())
		value = com->sAliasData;
	else if(!com->par)	//pokud prikaz nema parametry, nastavujeme hodnotu parametru podle toho, jestli je prikaz reverzni
		(com->reverse == NULL) ? value = string("1") : value = string("0");
	else
		value = par;
	(c->*(com->x))(value);	//provedeni prikazu
	return true;
}

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

	const int index = hash_function(com);
	command *var = fieldCom[index];

	//pridame novy prikaz
	if(!parameter)	//bez parametru
		fieldCom[index] = new command(com, func);
	else
		fieldCom[index] = new command(com, func, parameter);
	fieldCom[index]->next = var;
	fieldCom[index]->isDefault = true;	
	if(rev == NULL)
		return;
	if(rev->x != NULL && rev->reverse == NULL)	//pokud je reverzni, tak dostane odkaz na reverzni prvek
	{
		rev->reverse = fieldCom[index];
	}

	
}

/* Vytvoreni noveoh prikazu typu alias */
void commandsLibrary::addAliasCom(string com, void(CControl::*func)(string), string reverse,bool parameter, string aliasData)
{
	command *rev = getCommand(com);	//vyhledani zadaneho jmena v databazi
	if(rev != NULL && rev->isDefault) return;	//pokud tento prikaz uz existuje a je to standardni prikaz, nelze ho prepsat

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

	const int index = hash_function(com);
	command *var = fieldCom[index];	//vytvorime novy prikaz
	if(!parameter)
		fieldCom[index] = new command(com, func);	//bez parametru
	else
		fieldCom[index] = new command(com, func, parameter);	//s parametrem
	fieldCom[index]->next = var;
	fieldCom[index]->isDefault = false;
	if(!aliasData.empty())	//seznam prikazu k provedeni
		fieldCom[index]->sAliasData = aliasData;
	
	if(rev == NULL)
		return;
	if(rev->x != NULL && rev->reverse == NULL)	//pokud existuje reverzni prikaz
	{
		rev->reverse = fieldCom[index];	//nastavime ukazatel na rev. prikaz
	}

	
}

/* Hasovaci funkce */
int commandsLibrary::hash_function(const string s)
{
	int soucet = 0;
	const int length = static_cast<int>(s.length());
	for(int i = 0; i < length; i++)
		soucet+= s.at(i);
	return (soucet % HASH_COM_HEIGHT);
}

/* Vraci prikaz podle jeho jmena */
command *commandsLibrary::getCommand(string com)
{
	if(com == string(""))
		return NULL;
	const int index = hash_function(com);
	command *var = fieldCom[index];
	if(var == NULL)
		return NULL;
	
	while(var->com != com)
	{
		if(var->next == NULL)
			return NULL;
		var = var->next;
	}
	return var;
}
