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

//#include <crypt.h>
#include <cstdarg>
#include <cstdio>
#include <fstream>
#include <map>
#include <mysql/mysql.h>
#include "world.h"
#include "smysql.h"
#include "avatar.h"
#include "colorTable.h"
#include "stringutil.h"
#include "timestamp.h"
#include "stringutil.h"
#include "log.h"
#include "commandTable.h"

using namespace std;

// begin char class
bool Char::SetName( const string &name )
{
	string buf;

	// Validate name
	if ( name.length() < 3 || name.length() > 15 )
    {
		buf << name.length();
		return false;
	}


	buf << name;

	for ( string::iterator cChar = buf.begin(); cChar != buf.end(); ++cChar )
    {
		if ( !isalnum(*cChar) && (*cChar) != '\'' )
			return false;
	}

	// Name is good
	Set( "name", name );
	return true;
}

bool Char::Set( const string &set, const string &value )
{
	string buf;

	if ( !strcasecmp( set, "title" ) )
    {
		if ( value[0] != ',' && value[0] != '\'' && value[0] != ' ' )
			buf << " " << value;
   	   else
	  	    buf << value;

		_info[set] = buf;
	}
    else
		_info[set] = value;

	return true;
}

string Char::Get( const string &get )
{
    // capitalize name for god's sakes
    if ( get == "name" )
    {
      string s = _info[get];

      if ( s.empty() )
       return s;

      s[0] = toupper(s[0]);
      return s;
    }

	return _info[get];
}
// end char class

Avatar::Avatar( )
{
	_disconnected = false;
	_socket = new Socket;
	_gotInput = false;
	_status = LOGIN;
}

// get rid of the socket when avatar is deleted
// disconnect and remove from list & delete
Avatar::~Avatar( )
{
	if ( _socket )
    {
		if ( !_socket->GetSocketIP().empty() )
        {
			SocketServer::Instance()._socketList.remove( _socket );
			SocketServer::Instance().KillSocket( _socket ); 
			if ( Get( "name" ).empty() )
				cout << _socket->GetSocketIP() << " has disconnected @ " << Timestamp::Instance().GetDateTime() << "\n\r";
			else
				cout << Get( "name" ) << " has disconnected @ " << Timestamp::Instance().GetDateTime() << "\n\r";
		}
	}
		delete _socket;
}

bool Avatar::Load( )
{
    string query;
	Mysql::LMAP::iterator avatar_info;
    Mysql::RESULT z;

	query << "SELECT * FROM user WHERE username='" << Mysql::Instance().EscapeString( Get( "name" ) ) << "'";
	
	if ( !Mysql::Instance().Load( query.c_str() ) ) 
		return false;
		
   z = Mysql::Instance()._info;
   
   avatar_info = z[0].find("username");
   Set( "name", avatar_info->second );
   avatar_info = z[0].find("password");
   Set( "password", avatar_info->second );
   avatar_info = z[0].find("_group");
   Set( "group", avatar_info->second );
   avatar_info = z[0].find("reply");
   Set( "reply", avatar_info->second );
   avatar_info = z[0].find("title");
   Set( "title", avatar_info->second );
   avatar_info = z[0].find("login");
   Set( "login", avatar_info->second );
   avatar_info = z[0].find("prompt");
   Set( "prompt", avatar_info->second );
   avatar_info = z[0].find("version");
   Set( "version", avatar_info->second );
   
   avatar_info = z[0].find("last_room");
   Set( "last_room", avatar_info->second );
   
   _room = NULL;

   return true;
}

bool Avatar::Save( )
{
	string query;

	if ( Get( "group" ).empty() )
    {
    	Create();
    	return true;
    }

	// We have to escape certain strings because they may possibly have a single quote in them.
	query << "UPDATE user SET username='" << Mysql::Instance().EscapeString( Get( "name" ) ) << "', password='" << Mysql::Instance().EscapeString( Get( "password" ) ) << "', _group='" << Get( "group" ) << "',";
	query << " login='" << Mysql::Instance().EscapeString( Get( "login" ) ) << "', reply='" << Mysql::Instance().EscapeString( Get( "reply" ) ) << "', title='" << Mysql::Instance().EscapeString( Get( "title" ) ) << "',";
	query << " prompt='" << Mysql::Instance().EscapeString( Get( "prompt" ) ) << "', last_room=";

    if ( _room )
      query << _room->id;
    else
      query << Get("last_room");

    query << ", version=" << Get("version") << " WHERE username='" << Mysql::Instance().EscapeString( Get( "name" ) ) << "'";

    Mysql::Instance().RealQuery( query.c_str() );
    return true;
}

bool Avatar::Create( )
{
	string query;
	
	// We have to escape the name because it might have a single quote in it.. '
	query << "INSERT INTO user ( username, password ) VALUES ( '" << Mysql::Instance().EscapeString( Get( "name" ) ) << "', '" << Get( "password" ) << "' )";   
	
    Mysql::Instance().Query( query.c_str() );
    return true;
}

bool Avatar::Delete( )
{
	string query;
	
	query << "DELETE FROM user WHERE username='" << Mysql::Instance().EscapeString( Get( "name" ) ) << "'";	
	
	Mysql::Instance().Query( query.c_str() );
	return true;
}

STATUS Avatar::GetStatus( )
{
	return _status;
}

void Avatar::SetStatus( STATUS status )
{
	_status = status;
}

void Avatar::Send( char * message, ... )
{
	va_list args;
	char buf[MAX_BUFFER];
	string buf2;

    va_start( args, message );

	vsprintf( buf, message, args);
	buf2 = _socket->GetOutput();
	buf2.append( buf );
	
	_socket->SetOutput( buf2 );
	va_end( args );
}

void Avatar::Send( const string &message )
{
	string buf;

	buf.append( message );	
	_socket->SetOutput( buf );
}

void Avatar::SetSocket( Socket * socket )
{
	_socket = socket;
}

Socket * Avatar::GetSocket( )
{
	return _socket;
}

bool Avatar::VerifyPassword( const string &password )
{
	return Get( "password" ) == EncryptPassword( password );
}	

string Avatar::EncryptPassword( const string &password )
{
	return password; //crypt( password.c_str(), "$1$zetyadr" );
}

void Avatar::ClearPassword( )
{
	string blah = "e"; blah.erase();
	Set( "password", blah );
}
	
void Avatar::SetEdit( const string &edit )
{
	if ( edit.empty() )
		_edit.erase();
    else
		_edit = edit;
}

string Avatar::GetEdit( )
{
	return _edit;
}

bool Avatar::HasPassword( )
{
	return Get( "password" ).empty();
}

void Avatar::SetGotInput( bool value )
{
	_gotInput = value;
}

bool Avatar::GotInput( )
{
	return _gotInput;
}

void Avatar::FlushOutput( )
{
	string output;
	string buf2;
	string buf3;
	string colorClear;
	// This will add a prompt to the end of output
	// Ofcourse, one needs some text before it'll add a prompt
	// or we can add a prompt if we get some input and it calls for it. :)
	
	output = _socket->GetOutput();
	if ( output.empty() != true || _gotInput )
    {
		if ( output.find( string(_handlers.front()->Prompt( this ) )) != string::npos )
			return;
			
		//if ( this->waiting == false )  { // Don't send a prompt if there's delayed text going
			if ( !_gotInput )
					output.insert( 0, "\n\r" );
			else
            {
				// Only if there's no input set gotInput to false
				// we do it like this so there isn't an extra newline
				// everytime you get some output
				if ( _socket->GetInput().empty() )
					_gotInput = false;						
			}
			output.append( "\n\r" + _handlers.front()->Prompt( (this) ) );			
			
			_socket->SetOutput( output );
			
		//}
	}
	
	// This parses our color.
	colorClear << ColorTable::COLOR_ESCAPE << ColorTable::COLOR_CLEAR;
	buf3 = _socket->GetOutput();
	buf3.append( colorClear );
	
	for ( string::iterator cChar = buf3.begin(); cChar != buf3.end(); ++cChar )
    {
		if ( (*cChar) == ColorTable::COLOR_ESCAPE )
        {
			if ( ++cChar == buf3.end() ) 
				break;
			buf2 << ColorTable::Substitute( (*cChar) );
		}
        else
			buf2 << (*cChar); // There was no color this char.
	}
	_socket->SetOutput( buf2 );
}

void Avatar::SetDisconnected( bool value )
{
	// force save
    DoCmd( this, "save");

    // pull out of room
	this->_room->_avatarList.remove(this);

	_disconnected = value;
	_socket->SetDisconnected( true );
}

bool Avatar::IsDisconnected( )
{
	return _disconnected;
}

////////////////////// HANDLERS //////////////////////////
void Avatar::ReplaceHandler( Handler * handler )
{
	PopHandler( );
	StackHandler( handler );
}

void Avatar::StackHandler( Handler * handler )
{
	_handlers.push_front( handler );
	handler->Enter( this );
}

void Avatar::ExitHandler( )
{
	PopHandler( );
	_handlers.front()->Enter( this ) ;
}

void Avatar::PopHandler( )
{
	Handler * previous_handler = _handlers.front();
	_handlers.pop_front( );
	previous_handler->Exit( this );
}

void Avatar::HandleInput( const std::string &input )
{
	string::iterator cChar;
	string string1;
	string string2;
 
	string2 = input;
	for ( cChar = string2.begin(); cChar != string2.end(); ++cChar )
    {
		if ( ( (*cChar) == '\n' || (*cChar) == '\r' ) && cChar != string2.begin() )
        {
			_handlers.front()->Handle( this, string1 );
			string1.erase();
		}
        else
			string1 << (*cChar);
	}
			
	_handlers.front()->Handle( this, string1 );
}

// override
bool Avatar::Set( const string &set, const string &value )
{
	string buf;

	if ( !strcasecmp( set, "title" ) )
    {
		if ( value[0] != ',' && value[0] != '\'' && value[0] != ' ' )
			buf << " " << value;
   	   else
	  	    buf << value;

		_info[set] = buf;
	}
    else if ( !strcasecmp( set, "name" ) )
    {
		string buf;

		// Validate name
		if ( value.length() < 3 || value.length() > 15 )
        {
			buf << value.length();
			Send( "Names must be 3-15 characters in length.\n\r" );
			return false;
		}


		buf << value;

		for ( string::iterator cChar = buf.begin(); cChar != buf.end(); ++cChar )
        {
			if ( !isalnum(*cChar) && (*cChar) != '\'' )
            {
				Send( "Names may only contain: alpha-numeric '\n\r" );
				return false;
			}
		}
		_info[set] = buf;
	}
    else if ( !strcasecmp( set, "password" ) )
		_info[set] = value;
    else
		_info[set] = value;

	return true;
}
///////////////////// END HANDLERS //////////////////////