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

#include <list>
#include "smysql.h"
#include "commands.h"
#include "commandTable.h"
#include "avatar.h"
#include "world.h"
#include "handler.h"
#include "editHandlers.h"
#include "stringutil.h"
#include "split.h"
#include "timestamp.h"

// Player commands

using namespace std;

// Say
CmdSay::CmdSay( ) {
	SetName( "say" );
	SetShortcut( "'" );
	SetGroup( "player" );
	SetEnabled( true );
}

bool CmdSay::Execute( Avatar * avatar, const string &args ) {
	string args1;
	string buf;
	
	if ( args.empty() )
		return true;
		
	if ( args.length() > 500 ) {
		args1.append( args, 0, 500 ); 
	} else {
		args1 = args;
	}
	
	buf << "<" << avatar->Get( "name" ) << "> " << args1 << "{x\n\r";
	
	World::Instance().Broadcast( buf, WCONNECTED );
	return true;
}

// Who: Displays players online
CmdWho::CmdWho( ) {
	SetName( "who" );
	SetGroup( "player" );
	SetEnabled( true );
}

bool CmdWho::Execute( Avatar * avatar, const string &args ) {
	string buf,countt;
	int count = 0;
	list< Avatar *>::iterator a_it;
	
	for ( a_it = World::Instance()._avatarList.begin(); a_it != World::Instance()._avatarList.end(); ++a_it, count++ ) {
		if ( (*a_it)->GetStatus() != CONNECTED )
			continue;
			
		buf << "> " << (*a_it)->Get( "name" ) << (*a_it)->Get( "title" ) << "{x\n\r";
	}
	countt << count;
	if ( count > 1 )
	     buf.insert( 0, "-- " + countt + " Players --\n\r" );
	else
         buf.insert( 0, "-- " + countt + " Player --\n\r" );
	
	avatar->Send( buf );
	return true;
}

// Quit
CmdQuit::CmdQuit( ) {
	SetName( "quit" );
	SetGroup( "player" );
	SetEnabled( true );
}

bool CmdQuit::Execute( Avatar * avatar, const string &args ) {
	string buf;
	
	buf << "## " << avatar->Get( "name" ) << " has disconnected.\n\r";	
	World::Instance().Broadcast( buf, WCONNECTED );
	
	avatar->SetDisconnected( true );
	return true;
}

// Save
CmdSave::CmdSave( ) {
	SetName( "save" );
	SetGroup( "player" );
	SetEnabled( true );
}

bool CmdSave::Execute( Avatar * avatar, const string &args ) {
	avatar->Save( );
	avatar->Send( "You have been saved.\n\r" );
	return true;
}

// Emote
CmdEmote::CmdEmote( ) {
	SetName( "emote" );
	SetShortcut( "." );
	SetGroup( "player" );
	SetEnabled( true );
}

bool CmdEmote::Execute( Avatar * avatar, const string &args ) {
	string buf;
	char buf2[MAX_BUFFER];

	if ( args.empty() ) {
		avatar->Send( "What do you wish to emote?\n\r" );
		return true;
	}

	if ( args[0] != ',' && args[0] != '\'' ) {
		buf2[0] = ' ';
	    strcpy( buf2+1, args.c_str() );
    } else {
	   strcpy( buf2, args.c_str() );
    }

	 buf << avatar->Get( "name" ) << buf2 << "\n\r";

	World::Instance().Broadcast( buf, WCONNECTED );
	return true;
}

// title
CmdTitle::CmdTitle( ) {
	SetName( "title" );
	SetGroup( "player" );
	SetEnabled( true );
}

bool CmdTitle::Execute( Avatar * avatar, const string &args ) {
    avatar->Set( "title", args );
	avatar->Send( "Title set.\n\r" );
	return true;
}

// prompt
CmdPrompt::CmdPrompt( ) {
	SetName( "prompt" );
	SetGroup( "player" );
	SetEnabled( true );
}

bool CmdPrompt::Execute( Avatar * avatar, const string &args ) {
    string buf;
    
    if ( args.empty() ) {
    	buf << "Syntax: prompt <newprompt>\n\r{WCurrent Prompt: " << avatar->Get( "prompt" ) << "{x\n\r";
    	avatar->Send( buf );
    	return true;
    }
    
    for ( string::const_iterator cChar = args.begin(); cChar != args.end(); ++cChar ) { 
        if ( (*cChar) == '%' ) {
        	++cChar;
        	switch ( (*cChar) ) {
        		case 'R':        		   
        		case 'T':
        		case 's':
        		case 'c':
        		case '%':        	
        		   break;
        		default:
        		   buf << "Invalid escape code: " << (*cChar) << "\n\r";
        		   avatar->Send( buf );
        		   return true;
            }
        } 
    }
    buf = args;
    avatar->Set( "prompt", buf );
    avatar->Save();
	return true;
}

// commands
CmdCommands::CmdCommands( ) {
	SetName( "commands" );
	SetGroup( "player" );
	SetEnabled( true );
}

bool CmdCommands::Execute( Avatar * avatar, const string &args ) {
	list< Command *>::iterator it;	
	int f = 0;
	
	avatar->Send( "\t\t-- Commands -- \n\r" );
	for ( it = CommandTable::Instance()._commands.begin(); it != CommandTable::Instance()._commands.end(); ++it ) {
		if (  ( avatar->Get( "group" ).find( (*it)->_group ) != string::npos ) && ( (*it)->_enabled == true ) ) {
			if ( f == 0 ) {
			   avatar->Send( "  %-11s", (*it)->_name.c_str() );
			} else {
			   avatar->Send( "%-11s", (*it)->_name.c_str() );
			}
		    f++;
		    if ( f == 5 ) {
			   avatar->Send( "\n\r" );
			   f = 0;
			}
		}
	}
	avatar->Send( "{x\n\r" );	
	return true;
}

// Delete
CmdDelete::CmdDelete( ) {
	SetName( "delete" );
	SetGroup( "player" );
	SetEnabled( true );
}

bool CmdDelete::Execute( Avatar* avatar, const string &args ) {
	string buf;
	
	if ( args.empty() ) {
		avatar->Send( "Syntax: delete <password>\n\r" );
		return true;
	}
	
	if ( avatar->VerifyPassword( args ) ) {
		buf << "## " << avatar->Get( "name" ) << " has disconnected.\n\r";	
		World::Instance().Broadcast( buf, WCONNECTED );
		avatar->Delete();
		avatar->SetDisconnected( true );
	} else {
		avatar->Send( "That password is not correct.\n\r" );
	}
	return true;
}

// Password
CmdPassword::CmdPassword( ) {
	SetName( "password" );
	SetGroup( "player" );
	SetEnabled( true );
}

bool CmdPassword::Execute( Avatar* avatar, const string &args ) {
	string buf;
	string oldPassword;
	string newPassword;
	
	if ( args.empty() ) {
		avatar->Send( "Syntax: password <oldpassword> <newpassword>\n\r" );
		return true;
	}
	split( args, oldPassword, newPassword );
	if ( avatar->VerifyPassword( oldPassword ) ) {
		avatar->Set( "password", avatar->EncryptPassword( newPassword ) );
		avatar->Send( "Your password has been changed.\n\r" );
	} else {
		avatar->Send( "The old password is incorrect.\n\r" );
	}
	return true;
}

// Tell
CmdTell::CmdTell( ) {
	SetName( "tell" );
	SetGroup( "player" );
	SetEnabled( true );
}

bool CmdTell::Execute( Avatar* avatar, const string &args ) {
	string name;
	string arg;
	string message;
	Avatar * target;
	
	split( args, name, arg );
	if ( ( target = World::Instance().FindAvatar( name ) ) == NULL ) {
		avatar->Send( "That target cannot be found.\n\r" );
		return false;
	}
	
	
	message << "[From: " << avatar->Get( "name" ) << "] " << arg << "\n\r";
	target->Send( message );
	target->Set( "reply", avatar->Get( "name" ) );
	message.erase();
	message << "[To: " << target->Get( "name" ) << "] " << arg << "\n\r";
	avatar->Send( message );
	return true;
}

// Reply
CmdReply::CmdReply( ) {
	SetName( "reply" );
	SetShortcut( ";" );
	SetGroup( "player" );
	SetEnabled( true );
}

bool CmdReply::Execute( Avatar* avatar, const string &args ) {
	string name;
	string message;
	Avatar * target;
	
	if ( ( target = World::Instance().FindAvatar( avatar->Get( "reply" ) ) ) == NULL ) {
		avatar->Send( "That target cannot be found.\n\r" );
		return false;
	}
	
	
	message << "[From: " << avatar->Get( "name" ) << "] " << args << "\n\r";
	target->Send( message );
	target->Set( "reply", avatar->Get( "name" ) );
	message.erase();
	message << "[To: " << target->Get( "name" ) << "] " << args << "\n\r";
	avatar->Send( message );
	return true;
}

// ADMIN COMMANDS

// Buzz
CmdBuzz::CmdBuzz( ) {
	SetName( "buzz" );
	SetGroup( "admin" );
	SetEnabled( true );
}

bool CmdBuzz::Execute( Avatar * avatar, const string &args ) {
		string name;
	string arg;
	string message;
	Avatar * target;
	
	split( args, name, arg );
	if ( ( target = World::Instance().FindAvatar( name ) ) == NULL ) {
		avatar->Send( "That target cannot be found.\n\r" );
		return false;
	};
	message << "\n\r\a{RYou have been buzzed by {W" << avatar->Get( "name" ) << "{R at {W" << Timestamp::Instance().GetDateTime() << "{R.{x\a\n\r";
	target->Send( message );
	avatar->Send( "Your target has been buzzed!\n\r" );
	return true;
}


CmdEdit::CmdEdit( ) {
	SetName("edit");
	SetGroup("admin");
	SetEnabled( true );
}

bool CmdEdit::Execute( Avatar * avatar, const string &args ) {
	
	if ( !args.empty() ) {
		avatar->StackHandler( new EditAvatarHandler( avatar, args ) );
	} else {
		avatar->StackHandler( new EditAvatarHandler );
	}
	return true;
}

// Shutdown
CmdShutdown::CmdShutdown( ) {
	SetName( "shutdown" );
	SetGroup( "admin" );
	SetEnabled( true );
}

bool CmdShutdown::Execute( Avatar* avatar, const string &args ) {
	string buf;
	
	buf << "## Scratch is being shutdown.\n\r";	
	World::Instance().Broadcast( buf, WCONNECTED );
	World::Instance().Die();	
	return true;
}

// Copyover
CmdReboot::CmdReboot( ) {
	SetName( "reboot" );
	SetGroup( "admin" );
	SetEnabled( true );
}

bool CmdReboot::Execute( Avatar* avatar, const string &args ) {
	World::Instance().Copyover();	
	return true;
}

// Disable
CmdDisable::CmdDisable( ) {
	SetName( "disable" );
	SetGroup( "admin" );
	SetEnabled( true );	
}

bool CmdDisable::Execute( Avatar * avatar, const string &args ) {
	list< Command *>::iterator it;
	string command;
	string query;
		
	split( args, command );
	
	if ( args.empty() ) {
		Mysql::LMAP::iterator command_info;
		Mysql::RESULT::iterator commands;		
		query << "SELECT * FROM disabled_commands";
		
		if ( !Mysql::Instance().Load( query.c_str() ) ) {
			avatar->Send( "There are no disabled commands.\n\r" );
			return true;
		}
		
		query.erase();
		query << " -- Disabled Commands -- \n\r";
		for ( commands = Mysql::Instance()._info.begin(); commands != Mysql::Instance()._info.end(); ++commands ) {
			command_info = (*commands).find( "command" );
			query << "[" << command_info->second << " ";
			command_info = (*commands).find("disabler");
			query << command_info->second << "]\n\r";
		} 
		avatar->Send( query );
		return true;
	}
	
	// Either disable or enable the command; update the database
	for ( it = CommandTable::Instance()._commands.begin(); it != CommandTable::Instance()._commands.end(); ++it ) {
		if ( str_cmp( (*it)->_name, command ) ) {
			if ( (*it)->_enabled == true ) {
				(*it)->_enabled = false;
				query << "INSERT INTO disabled_commands SET command='" << (*it)->_name << "', disabler='" << avatar->Get( "name" ) << "' ";	

				if ( !Mysql::Instance().Query( query.c_str() ) )  {
					return false;
				}	
				avatar->Send( "Command disabled.\n\r" );
			} else {
				(*it)->_enabled = true;
				query << "DELETE FROM disabled_commands WHERE command='" << (*it)->_name << "'";	

				if ( !Mysql::Instance().Query( query.c_str() ) )  {
					return false;
				}
				avatar->Send( "Command enabled.\n\r" );
			}
			return true;
		}
	}
	avatar->Send( "That command does not exist.\n\r" );
	return true;
}

CmdSockets::CmdSockets( ) {
	SetName("sockets");
	SetGroup("admin");
	SetEnabled( true );
}

bool CmdSockets::Execute( Avatar * avatar, const string &args ) {
	string buf;
	list< Avatar * >::iterator it;
	buf << "\t\t-- Sockets --\n\r";
	for ( it = World::Instance()._avatarList.begin(); it != World::Instance()._avatarList.end(); ++it ) {
		buf << (*it)->Get( "name" ) << ": " << (*it)->GetSocket()->GetSocketIP() << " (descriptor: " << (*it)->GetSocket()->GetDescriptor() << ")\n\r";
	}
	avatar->Send( buf );
	return true;
}

// Disconnect
CmdDisconnect::CmdDisconnect( ) {
	SetName("disconnect");
	SetGroup("admin");
	SetEnabled( true );
}

bool CmdDisconnect::Execute( Avatar * avatar, const string &args ) {
	string starget;
	string arguments;
	Avatar * target;
	
	if ( args.empty() ) {
		avatar->Send( "Syntax: disconnect <player>\n\r" );
		return false;
	}
	
	split ( args, starget, arguments );
	
	if ( ( target = World::Instance().FindAvatar( starget ) ) == NULL ) {
		avatar->Send( "That target cannot be found.\n\r" );
		return false;
	}
		
	
	target->SetDisconnected( true );
	avatar->Send( "Your target has been disconnected.\n\r" ); 
	return true;
}