#include "iserver.h"

#include "socket.h"
#include "commands.h"
#include "tasserverdataformats.h"

#include <lsl/battle/ibattle.h>
#include <lsl/user/user.h>

#include <boost/typeof/typeof.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>


namespace LSL {

Server::Server()
    : m_impl(new ServerImpl(this))
{
    m_impl->m_sock->sig_doneConnecting.connect(
                boost::bind( &Server::OnSocketConnected, this, _1, _2 )
                );
}

Server::~Server()
{
    delete m_impl->m_sock;
}

void Server::Connect( const std::string& /*servername */,const std::string& addr, const int port )
{
    m_impl->m_buffer = "";
    m_impl->m_sock->Connect( addr, port );
    m_impl->m_sock->SetSendRateLimit( m_impl-> m_server_rate_limit );
    m_impl->m_connected = false;
    m_impl->m_online = false;
//    m_redirecting = false;
//    m_agreement = "";
    m_impl->m_crc.ResetCRC();
    std::string handle = m_impl->m_sock->GetHandle();
    if ( handle.length() > 0 ) m_impl->m_crc.UpdateData( handle + addr );
}

void Server::Disconnect(const std::string& reason)
{
    if ( !m_impl->m_connected )
    {
        return;
    }
    m_impl->_Disconnect(reason);
    m_impl->m_sock->Disconnect();
}

bool Server::IsOnline() const
{
    if ( !m_impl->m_connected ) return false;
    return m_impl->m_online;
}

bool Server::IsConnected()
{
    return (m_impl->m_sock->State() == Enum::SS_Open);
}

void Server::TimerUpdate()
{
	if ( !IsConnected() )
		return;
    if ( m_impl->m_sock->InTimeout( m_impl->m_ping_timeout ) )
	{
		sig_Timeout();
		Disconnect("timeout");
		return;
	}

	// joining battle with nat traversal:
	// if we havent finalized joining yet, and udp_reply_timeout seconds has passed since
	// we did UdpPing(our name) , join battle anyway, but with warning message that nat failed.
	// (if we'd receive reply from server, we'd finalize already)
	//
	time_t now = time(0);
    if ( m_impl->m_last_udp_ping > 0
            && (now -  m_impl->m_last_udp_ping) > m_impl->m_udp_reply_timeout )
	{
		sig_NATPunchFailed();
	}

	// Is it time for a nat traversal PING?
    if ( (  m_impl->m_last_udp_ping +  m_impl->m_keepalive ) < now )
	{
        m_impl->m_last_udp_ping = now;
		// Nat travelsal "ping"
		const ConstIBattlePtr battle=GetCurrentBattle();
		if (battle && !battle->InGame() )
		{
			if ( battle->GetNatType() == Enum::NAT_Hole_punching
				 || battle->GetNatType() == Enum::NAT_Fixed_source_ports  )
			{
				UdpPingTheServer();
				if ( battle->IsFounderMe() )
				{
					UdpPingAllClients();
				}
			}
		}
	}
}

void Server::SayChannel(const ChannelPtr channel, const std::string &msg)
{
    m_impl->SayChannel( channel->Name(), msg );
}

void Server::HandlePong( int replyid )
{
    PingList& pinglist = m_impl->GetPingList();
	PingList::iterator itor = pinglist.find(replyid);
	if ( itor != pinglist.end() )
	{
		sig_Pong( time(0) - itor->second );
		pinglist.erase( itor );
    }
}

void Server::JoinChannel( const std::string& channel, const std::string& key )
{
    m_impl->m_channel_pw[channel] = key;
    m_impl->JoinChannel(channel,key);
}

UserPtr Server::AcquireRelayhost()
{
    const unsigned int numbots = m_impl->m_relay_masters.size();
	if ( numbots > 0 )
	{
		srand ( time(NULL) );
		const unsigned int choice = rand() % numbots;
        m_impl->m_relay_host_manager = m_impl->m_relay_masters[choice];
        SayPrivate( m_impl->m_relay_host_manager, "!spawn" );
        return m_impl->m_relay_host_manager;
	}
	return UserPtr();
}

void Server::OpenBattle( Battle::BattleOptions bo )
{
	if ( bo.userelayhost )
	{
		AcquireRelayhost();
        m_impl->m_last_relay_host_password = bo.password;
	}

	if ( bo.nattype > 0 )
		UdpPingTheServer();
    m_impl->HostBattle(bo);
}

std::string Server::GenerateScriptPassword() const
{
	char buff[9];
	sprintf(buff,"%04x%04x", rand()&0xFFFF, rand()&0xFFFF);
	return std::string(buff);
}

void Server::JoinBattle( const IBattlePtr battle, const std::string& password )
{
	if (battle)
	{
		if ( battle->GetNatType() == Enum::NAT_Hole_punching
			 || battle->GetNatType() == Enum::NAT_Fixed_source_ports )
		{
			for (int n=0;n<5;++n) // do 5 udp pings with tiny interval
			{
				UdpPingTheServer( GetMe()->Nick() );
                 m_impl->m_last_udp_ping = time(0);
			}
		}
		srand ( time(NULL) );
        m_impl->JoinBattle(battle,password,GenerateScriptPassword());
    }
}

void Server::SayBattle(const IBattlePtr battle, const std::string &msg)
{
    if( battle )
        m_impl->SayBattle( battle->Id(), msg );
}

void Server::DoActionBattle(const IBattlePtr battle, const std::string &msg)
{
    if(battle)
        m_impl->DoActionBattle(battle->Id(), msg);
}

void Server::Ring(const ConstCommonUserPtr user)
{
    if(user)
        m_impl->Ring( user );
}

void Server::StartHostedBattle()
{
    if ( !m_impl->m_current_battle ) return;
    if ( !m_impl->m_current_battle ->IsFounderMe()) return;
    if ( m_impl->m_current_battle->GetNatType() == Enum::NAT_Hole_punching
         || m_impl->m_current_battle->GetNatType() == Enum::NAT_Fixed_source_ports )
	{
		UdpPingTheServer();
		for (int i=0;i<5;++i)
		{
			UdpPingAllClients();
		}
	}
    m_impl->StartHostedBattle();
    sig_StartHostedBattle( m_impl->m_current_battle->Id() );
}

void Server::LeaveBattle( const IBattlePtr battle)
{
    if(!battle)
        return;
    m_impl->m_relay_host_bot = UserPtr();
    m_impl->LeaveBattle(battle->Id());
}

void Server::AddBot(const IBattlePtr battle, const std::string& nick, UserBattleStatus& status )
{
    if (!battle) return;

    UTASBattleStatus tasbs;
    tasbs.tasdata = ConvTasbattlestatus( status );
    UTASColor tascl;
    tascl.color.red = status.color.Red();
    tascl.color.green = status.color.Green();
    tascl.color.blue = status.color.Blue();
    tascl.color.zero = 0;
    //ADDBOT name battlestatus teamcolor {AIDLL}
    std::string ailib;
    ailib += status.aishortname; // + "|" + status.aiversion;
    m_impl->SendCmd( "ADDBOT", nick + Util::ToString(tasbs.data) + " " + Util::ToString( tascl.data ) + " " + ailib );
}

void Server::RemoveBot( const IBattlePtr battle, const CommonUserPtr user )
{
    if (!battle) return;
    if (!user) return;
    UserBattleStatus status = user->BattleStatus();
    if (!status.IsBot()) return;

    //REMOVEBOT name
    m_impl->RelayCmd( "REMOVEBOT", user->Nick() );
}

void Server::UpdateBot( const IBattlePtr battle, const CommonUserPtr bot, const UserBattleStatus& status )
{
    if (!battle) return;
    if (!bot) return;
    if (!status.IsBot()) return;

    UTASBattleStatus tasbs;
    tasbs.tasdata = ConvTasbattlestatus( status );
    UTASColor tascl;
    tascl.color.red = status.color.Red();
    tascl.color.green = status.color.Green();
    tascl.color.blue = status.color.Blue();
    tascl.color.zero = 0;
    //UPDATEBOT name battlestatus teamcolor
    boost::format params( "%s %d %d");
    params % bot->Nick() % tasbs.data % tascl.data;
    if( !battle->IsProxy() )
        m_impl->SendCmd( "UPDATEBOT", params );
    else
        m_impl->RelayCmd( "UPDATEBOT", params );
}

UserVector Server::GetAvailableRelayHostList()
{
    if ( m_impl->m_relay_host_manager )
	{
		// this isn't blocking... so what is good for here?
        SayPrivate( m_impl->m_relay_host_manager, "!listmanagers" );
	}
    UserVector ret;
    for ( unsigned int i = 0; i < m_impl->m_relay_masters.size(); i++ )
	{
        UserPtr manager = m_impl->m_relay_masters[i];
		// skip the manager is not connected or reports it's ingame ( no slots available ), or it's away ( functionality disabled )
        if ( !manager ) continue;
		if ( manager->Status().in_game ) continue;
		if ( manager->Status().away ) continue;
		ret.push_back( manager );
	}
	return ret;
}

void Server::SetRelayIngamePassword( const CommonUserPtr user )
{
	if (!user) return;
    if ( !m_impl->m_current_battle ) return;
    if ( !m_impl->m_current_battle ->InGame() ) return;
        m_impl->RelayCmd( "SETINGAMEPASSWORD", user->Nick() + " " + user->BattleStatus().scriptPassword );
}

int Server::RelayScriptSendETA(const std::string& script)
{
    const StringVector strings = Util::StringTokenize( script, "\n");
    int relaylengthprefix = 10 + 1 + m_impl->m_relay_host_bot->Nick().length() + 2; // SAYPRIVATE + space + botname + space + exclamation mark length
	int length = script.length();
	length += relaylengthprefix + 11 + 1; // CLEANSCRIPT command size
	length += strings.size() * ( relaylengthprefix + 16 + 1 ); // num lines * APPENDSCRIPTLINE + space command size ( \n is already counted in script.size)
	length += relaylengthprefix + 9 + 1; // STARTGAME command size
    return length / m_impl->m_sock->GetSendRateLimit(); // calculate time in seconds to upload script
}

void Server::SendScriptToProxy( const std::string& script )
{
    const StringVector strings = Util::StringTokenize( script, "\n" );
    m_impl->RelayCmd( "CLEANSCRIPT" );
	for (StringVector::const_iterator itor; itor != strings.end(); ++itor)
	{
        m_impl->RelayCmd( "APPENDSCRIPTLINE", *itor );
	}
    m_impl->RelayCmd( "STARTGAME" );
}

//! @brief Send udp ping.
//! @note used for nat travelsal.

unsigned int Server::UdpPing(unsigned int src_port, const std::string &target, unsigned int target_port, const std::string &message)
{
	unsigned int result = 0;
	assert( false );
    return result;
}

void Server::SendOrRelayCmd(bool toggle, const std::string &command, const std::string &param)
{
    if (toggle)
        m_impl->RelayCmd(command, param);
    else
        m_impl->SendCmd(command, param);
}

void Server::UdpPingTheServer(const std::string &message)
{
    const unsigned int port = UdpPing( m_impl->m_udp_private_port,
                                 m_impl->m_addr,
                                 m_impl->m_nat_helper_port,
                                 message);
	if ( port>0 )
	{
        m_impl->m_udp_private_port = port;
        sig_MyInternalUdpSourcePort( m_impl->m_udp_private_port );
	}
}

// copypasta from spring.cpp , to get users ordered same way as in tasclient.
struct UserOrder
{
	int index;// user number for m_users.Get
	int order;// user order (we'll sort by it)
	bool operator<(UserOrder b) const  // comparison function for sorting
	{
		return order<b.order;
	}
};


void Server::UdpPingAllClients()
{
    if ( !m_impl->m_current_battle )return;
    if ( !m_impl->m_current_battle ->IsFounderMe())return;

	// I'm gonna mimic tasclient's behavior.
	// It of course doesnt matter in which order pings are sent,
	// but when doing "fixed source ports", the port must be
	// FIRST_UDP_SOURCEPORT + index of user excluding myself
	// so users must be reindexed in same way as in tasclient
	// to get same source ports for pings.


	// copypasta from spring.cpp
    CommonUserVector ordered_users = m_impl->m_current_battle->Users();
	//TODO this uses ptr diff atm
	std::sort(ordered_users.begin(),ordered_users.end());

    int i = -1;
    BOOST_FOREACH( const ConstCommonUserPtr user, ordered_users )
	{
        i++;
		if (!user)
			continue;
		const UserBattleStatus& status = user->BattleStatus();
        const std::string ip = status.ip;
        unsigned int port = status.udpport;
        const unsigned int src_port = m_impl->m_udp_private_port;
        if ( m_impl->m_current_battle->GetNatType() == Enum::NAT_Fixed_source_ports )
		{
			port = FIRST_UDP_SOURCEPORT + i;
		}

		if (port != 0 && ip.length() )
		{
			UdpPing(src_port, ip, port, "hai!" );
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
/////                       Internal Server Events                         /////
////////////////////////////////////////////////////////////////////////////////

void Server::OnSocketConnected(bool connection_ok, const std::string& msg)
{
    assert( connection_ok );//add proper error handling
    m_impl->m_connected = connection_ok;
    m_impl->m_online = false;
    m_impl->m_last_udp_ping = 0;
    m_impl->m_min_required_spring_ver = "";
    m_impl->m_relay_masters.clear();
    m_impl->GetLastPingID() = 0;
    m_impl->GetPingList().clear();
}

void Server::OnDisconnected()
{
    bool connectionwaspresent = m_impl->m_online ||
            !m_impl->m_last_denied.length() ||
            m_impl->m_redirecting;
    m_impl->m_connected = false;
    m_impl->m_online = false;
    m_impl->m_redirecting = false;
    m_impl->m_last_denied = "";
    m_impl->m_min_required_spring_ver = "";
    m_impl->m_relay_masters.clear();
    m_impl->GetLastPingID() = 0;
    m_impl->GetPingList().clear();
	// delete all users, battles, channels
	sig_Disconnected( connectionwaspresent );
}

void Server::OnSocketError( const Enum::SocketError& /*unused*/ )
{
}


void Server::OnProtocolError( const Enum::Protocolerror /*unused*/ )
{
}

////////////////////////////////////////////////////////////////////////////////
/////                        Command Server Events                         /////
////////////////////////////////////////////////////////////////////////////////
void Server::OnServerInitialData(const std::string& server_name, const std::string& server_ver, bool supported, const std::string& server_spring_ver, bool /*unused*/)
{
    m_impl->m_server_name = server_name;
    m_impl->m_server_ver = server_ver;
    m_impl->m_min_required_spring_ver = server_spring_ver;
}

void Server::OnNewUser( const UserPtr user )
{
    if (user->Nick() == "RelayHostManagerList" )
	{
        m_impl->m_relay_host_manager = user;
        SayPrivate( m_impl->m_relay_host_manager, "!lm" );
	}
}

void Server::OnUserStatus( const UserPtr user, UserStatus status )
{
	if ( !user ) return;
    UserStatus oldStatus = user->Status();
	user->SetStatus( status );
	//TODO: event
}

void Server::OnBattleStarted(const IBattlePtr battle)
{
    if (!battle) return;
    //TODO: event
}

void Server::OnBattleStopped(const IBattlePtr battle)
{
    if (!battle) return;
    //TODO: event
}

void Server::OnLogin(const UserPtr user )
{
    if (m_impl->m_online)
		return;
    m_impl->m_online = true;
    m_impl->m_me = user;
    m_impl->m_ping_thread = new PingThread( this, m_impl->m_ping_interval*1000 );
    m_impl->m_ping_thread->Init();
	//TODO: event
}

void Server::OnLogout()
{
	//TODO: event
}

void Server::OnUnknownCommand( const std::string& command, const std::string& params )
{
	//TODO: log
	//TODO: event
}

void Server::OnPong( long long ping_time )
{
	//TODO: event
}

void Server::OnUserQuit(const CommonUserPtr user)
{
	if ( !user ) return;
    if (user == m_impl->m_me) return;
    RemoveUser( user );
	//TODO: event
}

void Server::OnBattleOpened(const IBattlePtr battle )
{
    if ( battle && battle->GetFounder() ==
         m_impl->m_relay_host_bot )
	{
        battle->SetProxy( m_impl->m_relay_host_bot->Nick() );
        JoinBattle( battle, m_impl->m_last_relay_host_password ); // autojoin relayed host battles
	}
}

void Server::OnBattleMapChanged(const IBattlePtr battle, UnitsyncMap map)
{
	if (!battle) return;
	battle->SetHostMap( map.name, map.hash );
}

void Server::OnBattleModChanged( const IBattlePtr battle, UnitsyncMod mod )
{
	if (!battle) return;
	battle->SetHostMod( mod.name, mod.hash );
}

void Server::OnBattleMaxPlayersChanged( const IBattlePtr battle, int maxplayers )
{
	if (!battle) return;
	battle->SetMaxPlayers( maxplayers );
}

void Server::OnBattleHostChanged( const IBattlePtr battle, UserPtr host, const std::string& ip, int port )
{
	if (!battle) return;
    if (!host) battle->SetFounder( host->Nick() );
	battle->SetHostIp( ip );
	battle->SetHostPort( port );
}

void Server::OnBattleSpectatorCountUpdated(const IBattlePtr battle,int spectators)
{
	if (!battle) return;
    battle->SetSpectators(spectators);
}

void Server::OnUserJoinedBattle( const IBattlePtr battle, const UserPtr user )
{
	if (!battle) return;
	if (!user) return;
    if (battle->IsProxy())
        m_impl->RelayCmd("SUPPORTSCRIPTPASSWORD"); // send flag to relayhost marking we support script passwords
}

void Server::OnAcceptAgreement( const std::string& agreement )
{

}

void Server::OnRing( const UserPtr from )
{

}

void Server::OnChannelMessage( const ChannelPtr channel, const std::string& msg )
{
	if (!channel) return;
}

void Server::OnBattleLockUpdated(const IBattlePtr battle,bool locked)
{
	if (!battle) return;
	battle->SetLocked(locked);
}

void Server::OnUserLeftBattle(const IBattlePtr battle, const CommonUserPtr user)
{
	if (!user) return;
	//bool isbot = user->BattleStatus().IsBot();
	user->BattleStatus().scriptPassword.clear();
	if (!battle) return;
	battle->OnUserRemoved( user );
    if (user == m_impl->m_me)
	{
        m_impl->m_relay_host_bot = UserPtr();
	}
	//TODO: event
}

void Server::OnBattleClosed(const IBattlePtr battle )
{
    RemoveBattle( battle );
	//TODO:event
}

void Server::OnBattleDisableUnit( const IBattlePtr battle, const std::string& unitname, int count )
{
	if (!battle) return;
	battle->RestrictUnit( unitname, count );
	//TODO: event
}

void Server::OnBattleEnableUnit( const IBattlePtr battle, const StringVector& unitnames )
{
	if (!battle) return;
	BOOST_FOREACH( const std::string unit, unitnames )
	{
		battle->UnrestrictUnit( unit );
	}
	//TODO: event
}

void Server::OnBattleEnableAllUnits(const IBattlePtr battle )
{
	if (!battle) return;
	battle->UnrestrictAllUnits();
	//TODO: event
}


void Server::OnJoinChannelFailed( const ChannelPtr channel, const std::string& reason )
{
	if (!channel) return;
	//TODO: event
}

void Server::OnUserJoinedChannel( const ChannelPtr channel, const UserPtr user )
{
	if (!channel) return;
	if (!user) return;
	channel->OnChannelJoin( user );
	//TODO: event
}

void Server::OnKickedFromChannel( const ChannelPtr channel, const std::string& fromWho, const std::string& msg )
{
	if (!channel) return;
}

void Server::OnChannelSaid( const ChannelPtr channel, const CommonUserPtr user, const std::string& message )
{
    if ( m_impl->m_relay_host_bot != 0 &&
         channel == m_impl->m_channels.Get( "U" + Util::ToString(m_impl->m_relay_host_bot->Id()) ) )
	{
        if ( user == m_impl->m_me && message.length() > 0 && message[0] == '!')
			return;
        if ( user == m_impl->m_relay_host_bot )
		{
			if ( boost::starts_with(message, "UserScriptPassword") )
			{
				std::string msg_copy;
				GetWordParam( msg_copy ); // skip the command keyword
				std::string usernick = GetWordParam( msg_copy );
				std::string userScriptPassword = GetWordParam( msg_copy );
                UserPtr usr = m_impl->m_users.Get(usernick);
				if (!usr) return;
				OnUserScriptPassword(user, userScriptPassword);
				return;
			}
		}
	}
    if ( m_impl->m_relay_host_manager != 0 &&
         channel == m_impl->m_channels.Get( "U" + Util::ToString(m_impl->m_relay_host_manager->Id()) ) )
	{
        if ( user == m_impl->m_me &&  message.length() > 0 && message[0] == '!' )
			return;
        if ( user == m_impl->m_relay_host_manager )
		{
			if ( message.length() > 0 && message[0] == '\001' ) // error code
			{
			}
			else
			{
                m_impl->m_relay_host_bot = m_impl->m_users.FindByNick( message );
				return;
			}
		}
	}
//    makes no sense to me
//    if ( m_relay_masters.size() > 0
//         && channel == m_channels.Get( "U" + Util::ToString(m_relay_masters.Id() ) ) )
//	{
//		if ( user == m_me && message == "!lm" )
//			return;
//        if ( user == m_relay_masters )
//		{
//			if ( boost::starts_with(message,std::string("list ") ) )
//			{
//                std::string list = Util::AfterFirst( message, " " ) ;
//                m_relay_masters = Util::StringTokenize( list, "\t" );
//                return;
//			}
//		}
    //	}
}

void Server::OnChannelPart(ChannelPtr channel, UserPtr user, const std::string &message)
{
}

void Server::OnBattleStartRectAdd( const IBattlePtr battle, int allyno, int left, int top, int right, int bottom )
{
	if(!battle) return;
	battle->AddStartRect( allyno, left, top, right, bottom );
	battle->StartRectAdded( allyno );
}

void Server::OnBattleStartRectRemove( const IBattlePtr battle, int allyno )
{
	if (!battle) return;
	battle->RemoveStartRect( allyno );
	battle->StartRectRemoved( allyno );
}

void Server::OnFileDownload( bool autolaunch, bool autoclose, bool /*disconnectonrefuse*/, const std::string& FileName, const std::string& url, const std::string& description )
{
    // HUH?
//	UTASOfferFileData parsingdata;
//	parsingdata.data = GetIntParam( params );
}

void Server::OnBattleScript( const IBattlePtr battle, const std::string& script )
{
	if (!battle) return;
	battle->GetBattleFromScript( true );
}

void Server::OnMuteList(const ChannelPtr channel, const MuteList& mutelist )
{
}

void Server::OnKickedFromBattle( const IBattlePtr battle)
{
	if (!battle) return;
}


void Server::OnUserInternalUdpPort( const CommonUserPtr user, int udpport )
{
	if (!user) return;
}

void Server::OnUserExternalUdpPort( const CommonUserPtr user, int udpport )
{
	if (!user) return;
    user->BattleStatus().udpport = udpport;
}

void Server::OnUserIP( const CommonUserPtr user, const std::string& ip )
{
	if (!user) return;
	user->BattleStatus().ip = ip;
}

void Server::OnChannelJoinUserList( const ChannelPtr channel, const UserVector& users)
{

}

void Server::OnSelfHostedBattle(IBattlePtr battle )
{
}

void Server::OnSelfJoinedBattle( IBattlePtr battle )
{
}

void Server::OnSetBattleOption(IBattlePtr battle, const std::string &param, const std::string &value)
{
}

void Server::OnRequestBattleStatus(IBattlePtr battle)
{
//	if(!m_battle) return;
}

void Server::OnUserScriptPassword(const CommonUserPtr user, const std::string &pw)
{
    assert( false );
}

void Server::OnBattleHostchanged(IBattlePtr battle, int udpport)
{
}

void Server::OnUserBattleStatusUpdated(IBattlePtr battle, CommonUserPtr user, const UserBattleStatus &status)
{
}

int Server::GetNextAvailableID()
{
    return 1;
}

void Server::SayPrivate( const ConstCommonUserPtr user, const std::string& msg )
{
    m_impl->SayPrivate( user->Nick(), msg );
}

void Server::DoActionPrivate(const ConstCommonUserPtr user, const std::string& msg )
{
    m_impl->DoActionPrivate( user->Nick(), msg );
}

void Server::SendHostInfo(Enum::HostInfo update)
{
    m_impl->SendHostInfo(update);
}

void Server::SendHostInfo(const std::string &key)
{
    m_impl->SendHostInfo(key);
}

void Server::RemoveUser(const CommonUserPtr user)
{
    m_impl->m_users.Remove( user->key() );
}

void Server::RemoveChannel(const ChannelPtr chan)
{
    m_impl->m_channels.Remove( chan->key() );
}

void Server::RemoveBattle(const IBattlePtr battle)
{
    m_impl->m_battles.Remove( battle->key() );
}

void Server::SendMyBattleStatus( const UserBattleStatus& bs )
{
    UTASBattleStatus tasbs;
    tasbs.tasdata = ConvTasbattlestatus( bs );
    UTASColor tascl;
    tascl.color.red = bs.color.Red();
    tascl.color.green = bs.color.Green();
    tascl.color.blue = bs.color.Blue();
    tascl.color.zero = 0;
    //MYBATTLESTATUS battlestatus myteamcolor
    m_impl->SendCmd( "MYBATTLESTATUS", boost::format( "%d %d") % tasbs.data % tascl.data );
}

void Server::SendMyUserStatus()
{
    const UserStatus& us = GetMe()->Status();
    UTASClientStatus taus;
    taus.tasdata.in_game = us.in_game;
    taus.tasdata.away = us.away;
    taus.tasdata.rank = us.rank;
    taus.tasdata.moderator = us.moderator;
    taus.tasdata.bot = us.bot;
    m_impl->SendCmd( "MYSTATUS", Util::ToString( taus.byte ) );
}

void Server::ForceSide( const IBattlePtr battle, const CommonUserPtr user, int side )
{
    if (!battle) return;
    if (!user) return;
    UserBattleStatus status = user->BattleStatus();
    if (!m_impl->m_current_battle->IsFounderMe()) return;
    if ( user == m_impl->m_me )
    {
        status.side = side;
        SendMyBattleStatus( status );
        return;
    }

    if ( status.IsBot() )
    {
        status.side = side;
        UpdateBot( battle, user, status );
    }
}

void Server::ForceTeam( const IBattlePtr battle, const CommonUserPtr user, int team )
{
    if (!battle) return;
    if (!user) return;
    UserBattleStatus status = user->BattleStatus();
    if ( status.IsBot() )
    {
        status.team = team;
        UpdateBot( battle, user, status );
        return;
    }
    if ( user == m_impl->m_me )
    {
        status.team = team;
        SendMyBattleStatus( status );
        return;
    }
    if (!m_impl->m_current_battle->IsFounderMe()) return;

    //FORCETEAMNO username teamno
    SendOrRelayCmd( m_impl->m_current_battle->IsProxy(), "FORCETEAMNO", user->Nick() + " " + Util::ToString(team) );
}

void Server::ForceAlly( const IBattlePtr battle, const CommonUserPtr user, int ally )
{
    if (!battle) return;
    if (!user) return;
    UserBattleStatus status = user->BattleStatus();

    if ( status.IsBot() )
    {
        status.ally = ally;
        UpdateBot( battle, user, status );
        return;
    }

    if ( user == m_impl->m_me )
    {
        status.ally = ally;
        SendMyBattleStatus( status );
        return;
    }
    if (!m_impl->m_current_battle->IsFounderMe())
        return;
    //FORCEALLYNO username teamno
    SendOrRelayCmd( m_impl->m_current_battle->IsProxy(), "FORCEALLYNO", user->Nick() + " " + Util::ToString(ally) );
}

void Server::ForceColor(const IBattlePtr battle, const CommonUserPtr user, const lslColor& rgb)
{
    if (!battle) return;
    if (!user) return;
    UserBattleStatus status = user->BattleStatus();

    if ( status.IsBot() )
    {
        status.color = rgb;
        UpdateBot( battle, user, status );
        return;
    }
    if ( user == m_impl->m_me )
    {
        status.color = rgb;
        SendMyBattleStatus( status );
        return;
    }
    if (!m_impl->m_current_battle->IsFounderMe()) return;

    UTASColor tascl;
    tascl.color.red = rgb.Red();
    tascl.color.green = rgb.Green();
    tascl.color.blue = rgb.Blue();
    tascl.color.zero = 0;
    //FORCETEAMCOLOR username color
    SendOrRelayCmd( m_impl->m_current_battle->IsProxy(), "FORCETEAMCOLOR", user->Nick() + " " + Util::ToString( tascl.data ) );
}

void Server::ForceSpectator( const IBattlePtr battle, const CommonUserPtr user, bool spectator )
{
    if (!battle) return;
    if (!user) return;
    UserBattleStatus status = user->BattleStatus();

    if ( status.IsBot() )
    {
        status.spectator = spectator;
        UpdateBot( battle, user, status );
        return;
    }
    if ( user == m_impl->m_me )
    {
        status.spectator = spectator;
        SendMyBattleStatus( status );
        return;
    }
    if (!m_impl->m_current_battle->IsFounderMe()) return;

    //FORCESPECTATORMODE username
    SendOrRelayCmd( m_impl->m_current_battle->IsProxy(), "FORCESPECTATORMODE", user->Nick() );
}

void Server::BattleKickPlayer( const IBattlePtr battle, const CommonUserPtr user )
{
    if (!battle) return;
    if (!user) return;
    UserBattleStatus status = user->BattleStatus();

    if ( status.IsBot() )
    {
        RemoveBot( battle, user );
        return;
    }
    if ( user == m_impl->m_me )
    {
        LeaveBattle( battle );
        return;
    }
    if (!m_impl->m_current_battle->IsFounderMe()) return;

    if( !m_impl->m_current_battle->IsProxy() )
    {
        // reset his password to something random, so he can't rejoin
        user->BattleStatus().scriptPassword = (boost::format("%04x%04x") % (rand()&0xFFFF) % (rand()&0xFFFF) ).str();
        SetRelayIngamePassword( user );
    }
    //KICKFROMBATTLE username
    SendOrRelayCmd( m_impl->m_current_battle->IsProxy(),"KICKFROMBATTLE", user->Nick() );
}

void Server::SetHandicap( const IBattlePtr battle, const CommonUserPtr user, int handicap)
{
    if (!battle) return;
    if (!user) return;
    UserBattleStatus status = user->BattleStatus();

    if ( status.IsBot() )
    {
        status.handicap = handicap;
        UpdateBot( battle, user, status );
        return;
    }

    if (!m_impl->m_current_battle->IsFounderMe()) return;

    //HANDICAP username value
    SendOrRelayCmd( m_impl->m_current_battle->IsProxy(),"HANDICAP", user->Nick() + " " + Util::ToString(handicap) );
}

void Server::SendUserPosition( const CommonUserPtr user )
{
    if (!m_impl->m_current_battle) return;
    if (!m_impl->m_current_battle->IsFounderMe()) return;
    if (!user) return;

    UserBattleStatus status = user->BattleStatus();
    std::string msgx = "game/Team" + Util::ToString( status.team ) + "/StartPosX=" + Util::ToString( status.pos.x );
    std::string msgy = "game/Team" + Util::ToString( status.team ) + "/StartPosY=" + Util::ToString( status.pos.y );
    std::string netmessage = msgx + "\t" + msgy;
    m_impl->RelayCmd( "SETSCRIPTTAGS", netmessage );
}

void Server::SendScriptToClients( const std::string& script )
{
    m_impl->RelayCmd( "SCRIPTSTART" );
    const StringVector lines = Util::StringTokenize(script,"\n");
    for(StringVector::iterator itor; itor != lines.end(); ++itor)
    {
        m_impl->RelayCmd( "SCRIPT", *itor );
    }
    m_impl->RelayCmd( "SCRIPTEND" );
}


void Server::OnClientBattleStatus(IBattlePtr battle, UserPtr user, UserBattleStatus bstatus)
{
}

void Server::OnBattleEnableUnits(IBattlePtr battle, const StringVector unitlist)
{
}

void Server::OnUserStartPositionUpdated(IBattlePtr battle, CommonUserPtr player, const UserPosition &pos)
{
}

//**************Get/Setters ******************
IBattlePtr Server::GetCurrentBattle() { return m_impl->m_current_battle; }
const ConstIBattlePtr Server::GetCurrentBattle() const { return m_impl->m_current_battle; }

void Server::SetKeepaliveInterval( int seconds ) { m_impl->m_keepalive = seconds; }
int Server::GetKeepaliveInterval() { return m_impl->m_keepalive; }

std::string Server::GetRequiredSpring() const { return m_impl->m_min_required_spring_ver; }
void Server::SetRequiredSpring( const std::string& version ) { m_impl->m_min_required_spring_ver = version; }

const UserPtr Server::GetMe() const {return m_impl->m_me;}
std::string Server::GetServerName() const { return m_impl->m_server_name; }

void Server::SetPrivateUdpPort(int port) { m_impl->m_udp_private_port = port;}

void Server::OnUserLeftChannel(ChannelPtr channel, UserPtr user)
{
}

void Server::OnChannelAction(ChannelPtr channel, UserPtr user, const std::string &action)
{
}

void Server::OnChannelTopic(ChannelPtr channel, UserPtr user, const std::string &message)
{
}

void Server::Login(const std::string &user, const std::string &password)
{
    m_impl->Login( user, password );
}

//END **************Get/Setters ******************
} // namespace LSL
