///////////////////////////////////////////////////////////
///////////////// Have an itch? Scratch it! ///////////////
///////////////////////// SCRATCH /////////////////////////
/////////////////////  A MUD  Server   ////////////////////
///////////////////// By: Jared Devall ////////////////////
/////////////////////      Thanks:     ////////////////////
/////////////////////  DIKU/Merc/ROM   ////////////////////
///////////////////// Aetas/Deus Gang  ////////////////////
/////////////////////       Beej       ////////////////////
///////////////////////////////////////////////////////////

#include <map>
#include "commandTable.h"
#include "world.h"
#include "stringutil.h"
#include "split.h"
#include "log.h"

using namespace std;

// find's function and executes. If secret, no messages will be displayed
void DoCmd( Char *a, const string& cmd, bool shortcut, bool secret )
{
  Command *c = NULL;
  string arguments, command1;
  
  if ( !a || cmd.empty() )
   return;

  // pull off name and leave args
  split( cmd, command1, arguments );
  
  if ( (c = CommandTable::Instance().FindCommand( a, command1, shortcut, secret)) != NULL )
   c->Execute(a, arguments);
}


CommandTable::CommandTable( )
{
	LoadCommands( );	
}	

bool CommandTable::LoadCommands( )
{
	// Populate list with commands
	
	// directions go first...always!
	_commands.push_back( new CmdSouth );
	_commands.push_back( new CmdSouthEast );
	_commands.push_back( new CmdSouthWest );
	_commands.push_back( new CmdNorth );
	_commands.push_back( new CmdNorthEast );
	_commands.push_back( new CmdNorthWest );
	_commands.push_back( new CmdEast );
	_commands.push_back( new CmdWest );
	
	_commands.push_back( new CmdHelp );
	_commands.push_back( new CmdLook );
	_commands.push_back( new CmdChangeCmdGroup );
	
	// custom made editing commands
	_commands.push_back( new CmdHedit );
    _commands.push_back( new CmdRedit );
	
    // cmds that came preinstalled
	_commands.push_back( new CmdBuzz );
	_commands.push_back( new CmdCommands );
	_commands.push_back( new CmdDelete );
	_commands.push_back( new CmdDisable );
	_commands.push_back( new CmdDisconnect );
	_commands.push_back( new CmdEdit );
	_commands.push_back( new CmdEmote );
	_commands.push_back( new CmdPassword );
	_commands.push_back( new CmdPrompt );
	_commands.push_back( new CmdQuit );
	_commands.push_back( new CmdReboot );
	_commands.push_back( new CmdReply );
	_commands.push_back( new CmdSave );
	_commands.push_back( new CmdSay );
	_commands.push_back( new CmdShutdown );
	_commands.push_back( new CmdSockets );
	_commands.push_back( new CmdTell );
	_commands.push_back( new CmdTitle );
	_commands.push_back( new CmdWho );	
	
	// Make sure if any commands are supposed to be disabled we disable them
	LoadDisabledCommands( );
	return true;
}

bool CommandTable::LoadDisabledCommands( )
{
	string query;
	list< Command *>::iterator it;
	map< string, string >::iterator command;
	
	for ( it = _commands.begin(); it != _commands.end(); ++it ) {
		query.erase();
		query << "SELECT * FROM disabled_commands WHERE command='" << (*it)->_name << "'";			

		if ( !Mysql::Instance().Load( query.c_str() ) ) 
			continue;	
		
		// Found the command in the disabled database
    	(*it)->_enabled = false;			
    }
	return true;
}

Command * CommandTable::FindCommand( Char * avatar, const string &command, bool shortcut, bool secret )
{
	list< Command * >::iterator it;
	string command1 = command;

    // directional shortcuts
    if ( !strcasecmp( command.c_str(), "se") )
     command1 = "southeast";
    else if ( !strcasecmp( command.c_str(), "sw") )
     command1 = "southwest";
    else if ( !strcasecmp( command.c_str(), "nw") )
     command1 = "northwest";
    else if ( !strcasecmp( command.c_str(), "ne") )
     command1 = "northeast";


	for ( it = _commands.begin(); it != _commands.end(); ++it )
    {

		// If it's a shortcut, check against the commands shortcut
		if ( shortcut == true && !(*it)->_shortcut.empty() )
        {
			if ( ( str_cmp( (*it)->_shortcut, command1 ) ) && ( avatar->Get( "group" ).find( (*it)->_group ) != string::npos ) )
            {
				if ( ( (*it)->_enabled == false ) && (avatar->Get( "group" ).find( (*it)->_group ) != string::npos ) )
                {
                  if ( secret == false )
					avatar->Send( "That command is disabled.\n\r" );
					return NULL;
				}
                else if ( ( (*it)->_enabled == false ) && ( avatar->Get( "group" ).find( (*it)->_group ) == string::npos ) )
                {
                  if ( secret == false )
					avatar->Send( "That is an invalid command.\n\r" );
					return NULL;
				}
				return (*it);
			}
		}
		
		if ( (*it)->_enabled == false && ( str_cmp( (*it)->_name, command1 ) || ( str_prefix( command1, (*it)->_name ) ) ) && ( avatar->Get( "group" ).find( (*it)->_group ) != string::npos ) )
        {
			if ( avatar->Get( "group" ).find( (*it)->_group ) != string::npos )
			{
               if ( secret == false )
				avatar->Send( "That command is disabled.\n\r" );
            }
			else
            {
               if ( secret == false )
				avatar->Send( "That is an invalid command.\n\r" );
            }
			
			return NULL;
		}
		
		if ( ( str_cmp( (*it)->_name, command1 ) || ( str_prefix( command1, (*it)->_name ) ) ) && ( avatar->Get( "group" ).find( (*it)->_group ) != string::npos ) )
		   return (*it);
	}
	return NULL;
}

bool CommandTable::Execute( Char * avatar, const string &commandAndArgs, bool secret )
{
	string commandss = commandAndArgs;
	string command1;
	string arguments;
	Command * command;
	string buf;
	bool shortcut = false;
	list< Command * >::iterator it;
	

	if ( commandAndArgs.empty() || commandAndArgs[0] == '\n' || commandAndArgs[0] == '\r' )
		return false;


	// Check to see if there is a 'shortcut' used. Really checking if
	// it's a nonalphanumeric character.	
	if ( command1.empty() )
 	 for ( string::iterator cChar = commandss.begin(); cChar != commandss.end(); ++cChar )
     {
	 	 if ( !isalpha( (*cChar) ) && !isdigit( (*cChar) ) && cChar == commandss.begin() )
         {
		 	 command1 = (*cChar);
			 shortcut = true;
		 }
         else
		 	arguments << (*cChar);
	 }
	 
	 // No shortcut
	 if ( shortcut == false )
     {
		 arguments.erase();
		 split( commandAndArgs, command1, arguments );
	 }
	
	if ( ( command = FindCommand( avatar, command1, shortcut ) ) != NULL ) {
		command->Execute( avatar, arguments );
		return true;
	} 
   
    if ( secret == false )
    	avatar->Send( "That is an invalid command.\n\r" );

	return false;
}