/*
* networkmgr.cpp
*
* Copyright (C) 2003 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) 
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation (version 2 of the License)
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*
*/

#include <psconfig.h>
#include <csutil/csstring.h>
#include <iutil/vfs.h>
#include <behaviourlayer/behave.h>
#include <propclass/linmove.h>

#include "util/sleep.h"
#include "util/log.h"
#include "net/connection.h"
#include "net/messages.h"
#include "net/msghandler.h"
#include "net/npcmessages.h"
#include "engine/netpersist.h"
#include "util/eventmanager.h"
#include "networkmgr.h"
#include "globals.h"
#include "npcclient.h"
#include "npc.h"
#include "gem.h"
#include "util/serverconsole.h"
#include "tribe.h"

extern bool running;

NetworkManager::NetworkManager(MsgHandler *mh,psNetConnection* conn)
: reconnect(false)
{
    msghandler = mh;
    ready = false;
    connected = false;
    msghandler->Subscribe(this,MSGTYPE_NPCLIST);
    msghandler->Subscribe(this,MSGTYPE_MAPLIST);
    msghandler->Subscribe(this,MSGTYPE_CELPERSIST);
    msghandler->Subscribe(this,MSGTYPE_ALLENTITYPOS);
    msghandler->Subscribe(this,MSGTYPE_NPCOMMANDLIST);
    msghandler->Subscribe(this,MSGTYPE_PERSIST_ACTOR);
    msghandler->Subscribe(this,MSGTYPE_PERSIST_ITEM);
    msghandler->Subscribe(this,MSGTYPE_REMOVE_OBJECT);
    msghandler->Subscribe(this,MSGTYPE_DISCONNECT);
    msghandler->Subscribe(this,MSGTYPE_WEATHER);
    msghandler->Subscribe(this,MSGTYPE_MSGSTRINGS);
    msghandler->Subscribe(this,MSGTYPE_NEW_NPC);
    msghandler->Subscribe(this,MSGTYPE_NPC_SETOWNER);
    msghandler->Subscribe(this,MSGTYPE_NPC_COMMAND);

    msgstrings = NULL;
    connection= conn;

    PrepareMessage();
}

NetworkManager::~NetworkManager()
{
    if (msghandler)
    {
        msghandler->Unsubscribe(this,MSGTYPE_NPCLIST);
        msghandler->Unsubscribe(this,MSGTYPE_MAPLIST);
        msghandler->Unsubscribe(this,MSGTYPE_CELPERSIST);
        msghandler->Unsubscribe(this,MSGTYPE_ALLENTITYPOS);
        msghandler->Unsubscribe(this,MSGTYPE_NPCOMMANDLIST);
        msghandler->Unsubscribe(this,MSGTYPE_PERSIST_ACTOR);
        msghandler->Unsubscribe(this,MSGTYPE_PERSIST_ITEM);
        msghandler->Unsubscribe(this,MSGTYPE_REMOVE_OBJECT);
        msghandler->Unsubscribe(this,MSGTYPE_DISCONNECT);
        msghandler->Unsubscribe(this,MSGTYPE_WEATHER);
        msghandler->Unsubscribe(this,MSGTYPE_MSGSTRINGS);
        msghandler->Unsubscribe(this,MSGTYPE_NPC_SETOWNER);
        msghandler->Unsubscribe(this,MSGTYPE_NPC_COMMAND);
    }
}

void NetworkManager::Authenticate(csString& host,int port,csString& user,csString& pass)
{
    this->port = port;
    this->host = host;
    this->user = user;
    this->password = pass;

    psNPCAuthenticationMessage login(0,user,pass);
    msghandler->SendMessage(login.msg);
}

void NetworkManager::Disconnect()
{
    psDisconnectMessage discon(0, 0, "");
    msghandler->SendMessage(discon.msg);    
    connection->SendOut(); // Flush the network
    connection->DisConnect();
}

const char *NetworkManager::GetCommonString(uint32_t id)
{
    if (!msgstrings)
        return NULL;

    return msgstrings->Request(id);
}

void NetworkManager::HandleMessage(MsgEntry *me)
{
    switch ( me->GetType() )
    {
        case MSGTYPE_MAPLIST:
        {
            connected = true;
            if (ReceiveMapList(me))
            {
                RequestAllObjects();    
            }
            break;
        }
        case MSGTYPE_NPCLIST:
        {
            ReceiveNPCList(me);
            ready = true;
            break;
        }
        case MSGTYPE_PERSIST_ACTOR:
        {
            HandleActor( me );
            break;
        }        
        
        case MSGTYPE_PERSIST_ITEM:
        {
            HandleItem( me );
            break;
        }        
        
        case MSGTYPE_REMOVE_OBJECT:
        {
            HandleObjectRemoval( me );
            break;
        }

        case MSGTYPE_ALLENTITYPOS:
        {
            HandlePositionUpdates(me);
            break;
        }
        case MSGTYPE_NPCOMMANDLIST:
        {
            HandlePerceptions(me);
            break;
        }
        case MSGTYPE_MSGSTRINGS:
        {
            csRef<iEngine> engine = CS_QUERY_REGISTRY(npcclient->GetObjectReg(), iEngine);

            // receive message strings hash table
            psMsgStringsMessage msg(me);
            msgstrings = msg.msgstrings;
            connection->SetMsgStrings(msgstrings);
            connection->SetEngine(engine);
            break;
        }
        case MSGTYPE_DISCONNECT:
        {
            HandleDisconnect(me);
            break;
        }
        case MSGTYPE_WEATHER:
        {
            HandleTimeUpdate(me);
            break;
        }
        case MSGTYPE_NEW_NPC:
        {
            HandleNewNpc(me);
            break;
        }
        case MSGTYPE_NPC_SETOWNER:
        {
            HandleNPCSetOwner(me);
            break;
        }
        case MSGTYPE_NPC_COMMAND:
        {
            psServerCommandMessage msg(me);
            printf(msg.command.GetData());
            break;
        }
    }
}


void NetworkManager::HandleActor( MsgEntry* me )
{
    csRef<iEngine> engine = CS_QUERY_REGISTRY(npcclient->GetObjectReg(), iEngine);
    psPersistActor mesg( me, npcclient->GetNetworkMgr()->GetMsgStrings(), engine );

    gemNPCObject * obj = npcclient->FindEntityID(mesg.entityid);

    if(obj && obj->GetPlayerID() == mesg.playerID)
    {
        // We already know this entity so just update the entity.
        CPrintf(CON_DEBUG, "Already know about gemNPCActor: %s (%s), %u.\n", mesg.name.GetData(), obj->GetName().GetData(), mesg.entityid );
        obj->Move(mesg.pos, mesg.yrot, mesg.sectorName);
        obj->SetVisible( !mesg.flags & psPersistActor::INVISIBLE );
        
        return;
    }

    if(mesg.playerID != 0 && !obj)
        obj = npcclient->FindCharacterID(mesg.playerID);

    if(obj)
    {
        // We have a player id, entity id mismatch and we already know this entity
        // so we can only assume a RemoveObject message misorder and we will delete the existing one and recreate.
        CPrintf(CON_DEBUG, "Deleting because we already know gemNPCActor: %s (%s), EID: %u PID: %u as EID: %u PID: %u.\n", mesg.name.GetData(), obj->GetName().GetData(), mesg.entityid, mesg.playerID, obj->GetEntity()->GetID(), obj->GetPlayerID() );
        npcclient->Remove(obj->GetEntity()->GetID());
    }

    gemNPCActor* actor = new gemNPCActor( npcclient, mesg);    
    
    //    CPrintf(CON_DEBUG, "Notified about: %s (EID: %u) at %f %f %f\n", actor->GetName().GetData(), mesg.entityid ,mesg.pos.x,mesg.pos.y,mesg.pos.z);
    if ( actor->GetPlayerID() != 0 )
    {
        CPrintf(CON_DEBUG, "->Attaching to actor PID %d\n",actor->GetPlayerID() );
        npcclient->AttachNPC( actor, mesg.counter);                
    }
    
    npcclient->Add( actor );
}

void NetworkManager::HandleItem( MsgEntry* me )
{
    psPersistItem mesg(me);
    gemNPCObject * obj = npcclient->FindEntityID(mesg.id);

    if(obj)
    {
        // We already know this item so just update the position.
        CPrintf(CON_DEBUG, "Already know about gemNPCItem: %s (%s), %u.\n", mesg.name.GetData(), obj->GetName().GetData(), mesg.id );
        obj->Move(mesg.pos, mesg.yRot, mesg.sector);
        return;
    }

    gemNPCItem* item = new gemNPCItem( npcclient, mesg);        
    
    npcclient->Add( item );
}

void NetworkManager::HandleObjectRemoval( MsgEntry* me )
{
    psRemoveObject mesg(me);
    npcclient->Remove( mesg.object);
}

void NetworkManager::HandleTimeUpdate( MsgEntry* me )
{
    psWeatherMessage msg(me);

    if (msg.type == psWeatherMessage::DAYNIGHT)  // time update msg
    {
        TimePerception pcpt("time",msg.time);
        npcclient->TriggerEvent(NULL,&pcpt); // Broadcast
    }
}


void NetworkManager::RequestAllObjects()
{
    CPrintf(CON_DEBUG, "Requesting all game objects\n");    
    
    psRequestAllObjects mesg;
    msghandler->SendMessage( mesg.msg );
}


bool NetworkManager::ReceiveMapList(MsgEntry *msg)
{
    psMapListMessage list(msg);

    for (size_t i=0; i<list.map.Length(); i++)
    {
        if (!npcclient->LoadMap(list.map[i]))
            return false;
    }
    return true;
}

bool NetworkManager::ReceiveNPCList(MsgEntry *msg)
{
    uint32_t length, pid, eid;

    length = msg->GetUInt32();
    CPrintf(CON_DEBUG, "Received list of %i NPCs.\n", length);  
    for (unsigned int x=0; x<length; x++)
    {
        pid = msg->GetUInt32();
        eid = msg->GetUInt32();

        //gemNPCObject *actor = npcclient->FindCharacterID(pid);
        //if (actor)
        //    npcclient->AttachNPC(actor->GetActorPtr(), (uint8_t)-1);
    }

    return true;
}



void NetworkManager::HandlePositionUpdates(MsgEntry *msg)
{
    psAllEntityPosMessage updates(msg);

    csRef<iEngine> engine = CS_QUERY_REGISTRY(npcclient->GetObjectReg(), iEngine);

    for (int x=0; x<updates.count; x++)
    {
        csVector3 pos;
        PS_ID id;
        iSector* sector;

        updates.Get(id,pos, sector, npcclient->GetNetworkMgr()->GetMsgStrings(), engine);
        npcclient->SetEntityPos(id,pos, sector);
    }
}

void NetworkManager::HandlePerceptions(MsgEntry *msg)
{
    psNPCCommandsMessage list(msg);

    //    CPrintf(CON_DEBUG, "Got NPC Perception List:\n");

    char cmd = list.msg->GetInt8();
    while (cmd != psNPCCommandsMessage::CMD_TERMINATOR)
    {
        switch(cmd)
        {
            case psNPCCommandsMessage::PCPT_TALK:
            {
                PS_ID speaker = list.msg->GetUInt32();
                PS_ID target  = list.msg->GetUInt32();
                int faction   = list.msg->GetInt16();

                NPC *npc = npcclient->FindNPC(target);
                if (!npc)
                {
                    CPrintf(CON_DEBUG, "Got talk perception for unknown NPC!\n");
                    break;
                }
                iCelEntity *speaker_ent = npcclient->FindEntity(speaker);
                if (!speaker_ent)
                {
                    CPrintf(CON_DEBUG, "%s got talk perception from unknown speaker!\n", npc->GetName().GetData());
                    break;
                }

                FactionPerception talk("talk",faction,speaker_ent);
                CPrintf(CON_DEBUG, "Got Talk perception for NPC %s, from actor %s, faction diff=%d.\n",
                    npc->GetEntity()->GetName(),speaker_ent->GetName(),faction);

                npcclient->TriggerEvent(npc,&talk);
                break;
            }
            case psNPCCommandsMessage::PCPT_ATTACK:
            {
                PS_ID target   = list.msg->GetUInt32();
                PS_ID attacker = list.msg->GetUInt32();

                NPC *npc = npcclient->FindNPC(target);
                iCelEntity *attacker_ent = npcclient->FindEntity(attacker);
                
                if (!npc)
                {
                    CPrintf(CON_DEBUG, "Got attack perception for unknown NPC!\n");
                    break;
                }
                if (!attacker_ent)
                {
                    CPrintf(CON_ERROR, "%s got attack perception for unknown attacker (EID: %u)!\n",npc->GetName().GetData(), attacker );
                    break;
                }

                AttackPerception attack("attack",attacker_ent);
                CPrintf(CON_DEBUG, "Got Attack perception for NPC %s, from actor %s.\n",
                    npc->GetEntity()->GetName(),attacker_ent->GetName());

                npcclient->TriggerEvent(npc,&attack);
                break;
            }
            case psNPCCommandsMessage::PCPT_GROUPATTACK:
            {
                PS_ID target   = list.msg->GetUInt32();
                NPC *npc = npcclient->FindNPC(target);

                int groupCount = list.msg->GetUInt8();
                csArray<iCelEntity *> attacker_ents(groupCount);
                csArray<int> bestSkillSlots(groupCount);
                for (int i=0; i<groupCount; i++)
                {
                    attacker_ents.Push(npcclient->FindEntity(list.msg->GetUInt32()));
                    bestSkillSlots.Push(list.msg->GetInt8());
                    if(!attacker_ents.Top())
                    {
                        if(npc)
                            CPrintf(CON_DEBUG, "%s got group attack perception for unknown group member!\n",npc->GetEntity()->GetName() );
                        attacker_ents.Pop();
                        bestSkillSlots.Pop();
                    }
                }


                if (!npc)
                {
                    CPrintf(CON_DEBUG, "Got group attack perception for unknown NPC!\n");
                    break;
                }


                if(attacker_ents.Length() == 0)
                {
                    CPrintf(CON_DEBUG, "%s got group attack perception and all group members are unknown!\n",npc->GetName().GetData() );
                    break;
                }

                GroupAttackPerception attack("attack",attacker_ents,bestSkillSlots);
                CPrintf(CON_DEBUG, "Got Group Attack perception for NPC %s, recognising %i actors in the group.\n",
                    npc->GetEntity()->GetName(),attacker_ents.Length());

                npcclient->TriggerEvent(npc,&attack);
                break;
            }

            case psNPCCommandsMessage::PCPT_DMG:
            {
                PS_ID attacker = list.msg->GetUInt32();
                PS_ID target   = list.msg->GetUInt32();
                float dmg      = list.msg->GetFloat();

                NPC *npc = npcclient->FindNPC(target);
                if (!npc)
                {
                    CPrintf(CON_DEBUG, "Attack on unknown npc.\n");
                    break;
                }
                iCelEntity *attacker_ent = npcclient->FindEntity(attacker);
                if (!attacker_ent)
                {
                    CPrintf(CON_ERROR, "%s got attack perception for unknown attacker! (EID: %u)\n",npc->GetName().GetData(), attacker );
                    break;
                }

                DamagePerception damage("damage",attacker_ent,dmg);
                CPrintf(CON_DEBUG, "Got Damage perception for NPC %s, from actor %s for %1.1f HP.\n",
                    npc->GetEntity()->GetName(),attacker_ent->GetName(),dmg);

                npcclient->TriggerEvent(npc,&damage);
                break;
            }
            case psNPCCommandsMessage::PCPT_DEATH:
            {
                PS_ID who = list.msg->GetUInt32();
                NPC *npc = npcclient->FindNPC(who);
                if (!npc) // Not managed by us, or a player
                {
                    CPrintf(CON_DEBUG, "Got Death message for human player %u\n",who);
                    DeathPerception pcpt("death",who);
                    npcclient->TriggerEvent(NULL,&pcpt); // Broadcast
                    break;
                }
                CPrintf(CON_DEBUG, "Got Death message for %s\n",npc->GetName().GetData());
                npcclient->HandleDeath(npc);
                break;
            }
            case psNPCCommandsMessage::PCPT_SPELL:
            {
                PS_ID caster = list.msg->GetUInt32();
                PS_ID target = list.msg->GetUInt32();
                uint32_t strhash = list.msg->GetUInt32();
                float    severity = list.msg->GetInt8() / 10;
                csString type = GetCommonString(strhash);

                iCelEntity *caster_ent = npcclient->FindEntity(caster);
                NPC *npc = npcclient->FindNPC(target);
                iCelEntity *target_ent = (npc) ? npc->GetEntity() : npcclient->FindEntity(target);

                CPrintf(CON_DEBUG,"Got Spell Perception for %s\n",(caster_ent)?caster_ent->GetName():"(unknown entity)");

                if (!caster_ent || !target_ent)
                    break;

                iSector *sector;
                csVector3 pos;
                float yrot;
                psGameObject::GetPosition((caster_ent)?caster_ent:target_ent,pos,yrot,sector);

                SpellPerception pcpt("spell",caster_ent,target_ent,type,severity);

                npcclient->TriggerEvent(NULL,&pcpt,20,&pos,sector);
                break;
            }
            case psNPCCommandsMessage::PCPT_LONGRANGEPLAYER:
            case psNPCCommandsMessage::PCPT_SHORTRANGEPLAYER:
            case psNPCCommandsMessage::PCPT_VERYSHORTRANGEPLAYER:
            {
                PS_ID npcid   = list.msg->GetUInt32();
                PS_ID player  = list.msg->GetUInt32();
                float faction = list.msg->GetFloat();

                CPrintf(CON_DEBUG,"Range perception npc: %d, player: %d, faction:%.0f\n", npcid, player, faction);

                NPC *npc = npcclient->FindNPC(npcid);
                if (!npc)
                    break;  // This perception is not our problem

                iCelEntity *npc_ent = (npc) ? npc->GetEntity() : npcclient->FindEntity(npcid);
                iCelEntity *player_ent = npcclient->FindEntity(player);

                if (!player_ent || !npc_ent)
                    break;

                CPrintf(CON_DEBUG,"Got Player %s in Range of %s Perception, with faction %.0f\n",
                        player_ent->GetName(), npc_ent->GetName(), faction);

                csString pcpt_name;
                if ( npc->GetOwner() == player_ent )
                {
                    pcpt_name.Append("owner ");
                }
                else
                {
                    pcpt_name.Append("player ");
                }
                if (cmd == psNPCCommandsMessage::PCPT_LONGRANGEPLAYER)
                    pcpt_name.Append("sensed");
                if (cmd == psNPCCommandsMessage::PCPT_SHORTRANGEPLAYER)
                    pcpt_name.Append("nearby");
                if (cmd == psNPCCommandsMessage::PCPT_VERYSHORTRANGEPLAYER)
                    pcpt_name.Append("adjacent");

        // @@@ Jorrit: cast to in ok below?
                FactionPerception pcpt(pcpt_name, int (faction), player_ent);

                npcclient->TriggerEvent(npc,&pcpt);
                break;
            }
            case psNPCCommandsMessage::PCPT_OWNER_CMD:
            {
                PS_ID command = list.msg->GetUInt32();
                PS_ID owner_id = list.msg->GetUInt32();
                PS_ID pet_id = list.msg->GetUInt32();

                iCelEntity *owner = npcclient->FindEntity(owner_id);
                NPC *npc = npcclient->FindNPC(pet_id);
                
                iCelEntity *pet = (npc) ? npc->GetEntity() : npcclient->FindEntity( pet_id );

                CPrintf(CON_DEBUG,"Got OwnerCmd %d Perception from %s for %s\n",command,(owner)?owner->GetName():"(unknown entity)",(pet)?pet->GetName():"(unknown entity)");

                if (!owner || !pet)
                    break;

                iSector *sector;
                csVector3 pos;
                float yrot;
                psGameObject::GetPosition((owner)?owner:pet,pos,yrot,sector);

                OwnerCmdPerception pcpt( "OwnerCmdPerception", command, owner, pet );

                npcclient->TriggerEvent( npc, &pcpt);
                break;
            }
            case psNPCCommandsMessage::PCPT_OWNER_ACTION:
            {
                PS_ID action = list.msg->GetUInt32();
                PS_ID owner_id = list.msg->GetUInt32();
                PS_ID pet_id = list.msg->GetUInt32();

                iCelEntity *owner = npcclient->FindEntity(owner_id);
                NPC *npc = npcclient->FindNPC(pet_id);
                iCelEntity *pet = (npc) ? npc->GetEntity() : npcclient->FindEntity( pet_id );

                CPrintf(CON_DEBUG,"Got OwnerAction %d Perception from %s for %s\n",action,(owner)?owner->GetName():"(unknown entity)",(pet)?pet->GetName():"(unknown entity)");

                if (!owner || !pet)
                    break;

                iSector *sector;
                csVector3 pos;
                float yrot;
                psGameObject::GetPosition((owner)?owner:pet,pos,yrot,sector);

                OwnerActionPerception pcpt( "OwnerActionPerception", action, owner, pet );

                npcclient->TriggerEvent( npc, &pcpt);
                break;
            }
            case psNPCCommandsMessage::PCPT_INVENTORY:
            {
                PS_ID owner_id = msg->GetUInt32();
                csString item_name = msg->GetStr();
                bool inserted = msg->GetBool();

                iCelEntity *owner = npcclient->FindEntity(owner_id);
                NPC *npc = npcclient->FindNPC(owner_id);

                npc->Printf("Got Inventory %s Perception from %s for %s\n",(inserted?"Add":"Remove"),(owner)?owner->GetName():"(unknown entity)",item_name.GetData());

                if (!owner || !npc)
                    break;
                /* TODO: Create a inventory for each NPC.
                if (inserted)
                {
                    npc->InventoryAdd(item_name);
                }
                else
                {
                    npc->InventoryRemove(item_name);
                }
                */
                csString str;
                str.Format("inventory:%s",item_name.GetData());

                Perception pcpt( str );

                npcclient->TriggerEvent( npc, &pcpt);

                // Hack: To get inventory to tribe. Need some more general way of
                //       delivery of perceptions to tribes....
                if (npc->GetTribe())
                {
                    npc->GetTribe()->HandlePerception(str);
                }
                // ... end of hack.
                break;
            }
            
            default:
            {
                CPrintf(CON_ERROR,"************************\nUnknown npc cmd: %d\n*************************\n",cmd);
                break;
            }
        }
        cmd = list.msg->GetInt8();
    }
}

void NetworkManager::HandleDisconnect(MsgEntry *msg)
{
    psDisconnectMessage disconnect(msg);

    // Special case to drop login failure reply from server immediately after we have already logged in.
    if (connected && disconnect.msgReason.CompareNoCase("Already Logged In."))
        return;

    CPrintf(CON_DEBUG, "Disconnected: %s\n",disconnect.msgReason.GetData());

    // Reconnect is disabled right now because the CEL entity registry is not flushed upon
    // reconnect which will cause BIG problems.
    Disconnect();
    abort();
    ServerConsole::Abort();
    if(!reconnect && false)
    {
        connected = ready = false;

        // reconnect
        connection->DisConnect();
        npcclient->RemoveAll();

        reconnect = true;
        // 60 secs to allow any connections to go linkdead.
        psNPCReconnect *recon = new psNPCReconnect(60000, this, false);
        npcclient->GetEventMgr()->Push(recon);
    }
}

void NetworkManager::HandleNewNpc(MsgEntry *me)
{
    printf("*****Got new NPC notification\n");
    psNewNPCCreatedMessage msg(me);
    printf("*****Master=%d, New=%d\n",msg.master_id,msg.new_npc_id);

    NPC *npc = npcclient->FindNPC(msg.master_id);
    if (npc)
    {
        // Insert a row in the db for this guy next.  
        // We will get an entity msg in a second to make him come alive.
        npc->InsertCopy(msg.new_npc_id);

        // Now requery so we have the new guy on our list when we get the entity msg.
        if (!npcclient->ReadSingleNPC(msg.new_npc_id))
        {
            Error3("Error creating copy of master %d as id %d.",msg.master_id,msg.new_npc_id);
            return;
        }

        //gemNPCObject *actor = npcclient->FindCharacterID(msg.new_npc_id);  // see if we already got entity
        //if (actor)
        //    npcclient->AttachNPC(actor->GetActorPtr(), (uint8_t)-1);
    }
    else
    {
        // Ignore it here.  We don't manage the master.
    }
}

void NetworkManager::HandleNPCSetOwner(MsgEntry *me)
{

    printf("*****Got NPC Owner notification\n");
    psNPCSetOwnerMessage msg(me);
    printf("*****Master=%d, Pet=%d\n",msg.master_clientid,msg.npcpet_id);

    NPC *npc = npcclient->FindNPC( msg.npcpet_id );
    if ( npc )
        npc->SetOwnerID( msg.master_clientid );
}

void NetworkManager::PrepareMessage()
{
    outbound = new psNPCCommandsMessage(0,30000);
    cmd_count = 0;
}

void NetworkManager::QueueDRData(iCelEntity *entity,iPcLinearMovement *linmove,uint8_t counter)
{

    if ( outbound->msg->current > ( outbound->msg->bytes->GetSize() - 100 ) )
    {
        CPrintf(CON_DEBUG, "Sent all commands [%d] due to possible Message overrun.\n", cmd_count );
        SendAllCommands();
    }

    outbound->msg->Add( (int8_t) psNPCCommandsMessage::CMD_DRDATA);

    psDRMessage drmsg(0,entity->GetID(),counter,msgstrings,linmove);

    outbound->msg->Add( drmsg.msg->bytes->payload,(uint32_t)drmsg.msg->bytes->GetTotalSize() );
    if ( outbound->msg->overrun )
    {
        CS_ASSERT(!"NetworkManager::QueueDRData put message in overrun state!\n");
    }
    cmd_count++;
}

void NetworkManager::QueueAttackCommand(iCelEntity *attacker, iCelEntity *target)
{

    if ( outbound->msg->current > ( outbound->msg->bytes->GetSize() - 100 ) )
    {
        CPrintf(CON_DEBUG, "Sent all commands [%d] due to possible Message overrun.\n", cmd_count );
        SendAllCommands();
    }

    if (target)
        CPrintf(CON_DEBUG, "%s is attacking %s.\n",attacker->GetName(),target->GetName() );
    else
        CPrintf(CON_DEBUG, "%s stops attacking.\n",attacker->GetName() );

    outbound->msg->Add( (int8_t) psNPCCommandsMessage::CMD_ATTACK);
    outbound->msg->Add( (uint32_t) attacker->GetID() );
    
    if (target)
        outbound->msg->Add( (uint32_t) target->GetID() );
    else
        outbound->msg->Add( (uint32_t) 0 );  // 0 target means stop attack

    if ( outbound->msg->overrun )
    {
        CS_ASSERT(!"NetworkManager::QueueAttackCommand put message in overrun state!\n");
    }

    cmd_count++;
}

void NetworkManager::QueueSpawnCommand(iCelEntity *mother, iCelEntity *father)
{
    if ( outbound->msg->current > ( outbound->msg->bytes->GetSize() - 100 ) )
    {
        CPrintf(CON_DEBUG, "Sent all commands [%d] due to possible Message overrun.\n", cmd_count );
        SendAllCommands();
    }

    outbound->msg->Add( (int8_t) psNPCCommandsMessage::CMD_SPAWN);
    outbound->msg->Add( (uint32_t) mother->GetID() );
    outbound->msg->Add( (uint32_t) father->GetID() );

    if ( outbound->msg->overrun )
    {
        CS_ASSERT(!"NetworkManager::QueueSpawnCommand put message in overrun state!\n");
    }
    cmd_count++;
}

void NetworkManager::QueueTalkCommand(iCelEntity *speaker, const char* text)
{
    if ( outbound->msg->current > ( outbound->msg->bytes->GetSize() - 100 ) )
    {
        CPrintf(CON_DEBUG, "Sent all commands [%d] due to possible Message overrun.\n", cmd_count );
        SendAllCommands();
    }

    outbound->msg->Add( (int8_t) psNPCCommandsMessage::CMD_TALK);
    outbound->msg->Add( (uint32_t) speaker->GetID() );
    
    outbound->msg->Add(text);
    if ( outbound->msg->overrun )
    {
        CS_ASSERT(!"NetworkManager::QueueTalkCommand put message in overrun state!\n");
    }

    cmd_count++;
}

void NetworkManager::QueueVisibilityCommand(iCelEntity *entity, bool status)
{
    if ( outbound->msg->current > ( outbound->msg->bytes->GetSize() - 100 ) )
    {
        CPrintf(CON_DEBUG, "Sent all commands [%d] due to possible Message overrun.\n", cmd_count );
        SendAllCommands();
    }

    outbound->msg->Add( (int8_t) psNPCCommandsMessage::CMD_VISIBILITY);
    outbound->msg->Add( (uint32_t) entity->GetID() );
    
    outbound->msg->Add(status);
    if ( outbound->msg->overrun )
    {
        CS_ASSERT(!"NetworkManager::QueueVisibleCommand put message in overrun state!\n");
    }

    cmd_count++;
}

void NetworkManager::QueuePickupCommand(iCelEntity *entity, iCelEntity *item, int count)
{
    if ( outbound->msg->current > ( outbound->msg->bytes->GetSize() - 100 ) )
    {
        CPrintf(CON_DEBUG, "Sent all commands [%d] due to possible Message overrun.\n", cmd_count );
        SendAllCommands();
    }

    outbound->msg->Add( (int8_t) psNPCCommandsMessage::CMD_PICKUP);
    outbound->msg->Add( (uint32_t) entity->GetID() );
    outbound->msg->Add( (uint32_t) item->GetID() );
    outbound->msg->Add( (int16_t) count );

    if ( outbound->msg->overrun )
    {
        CS_ASSERT(!"NetworkManager::QueuePickupCommand put message in overrun state!\n");
    }

    cmd_count++;
}

void NetworkManager::QueueEquipCommand(iCelEntity *entity, csString item, csString slot, int count)
{
    if ( outbound->msg->current > ( outbound->msg->bytes->GetSize() - 100 ) )
    {
        CPrintf(CON_DEBUG, "Sent all commands [%d] due to possible Message overrun.\n", cmd_count );
        SendAllCommands();
    }

    outbound->msg->Add( (int8_t) psNPCCommandsMessage::CMD_EQUIP);
    outbound->msg->Add( (uint32_t) entity->GetID() );
    outbound->msg->Add( item );
    outbound->msg->Add( slot );
    outbound->msg->Add( (int16_t) count );

    if ( outbound->msg->overrun )
    {
        CS_ASSERT(!"NetworkManager::QueueEquipCommand put message in overrun state!\n");
    }

    cmd_count++;
}

void NetworkManager::QueueDequipCommand(iCelEntity *entity, csString slot)
{
    if ( outbound->msg->current > ( outbound->msg->bytes->GetSize() - 100 ) )
    {
        CPrintf(CON_DEBUG, "Sent all commands [%d] due to possible Message overrun.\n", cmd_count );
        SendAllCommands();
    }

    outbound->msg->Add( (int8_t) psNPCCommandsMessage::CMD_DEQUIP);
    outbound->msg->Add( (uint32_t) entity->GetID() );
    outbound->msg->Add( slot );

    if ( outbound->msg->overrun )
    {
        CS_ASSERT(!"NetworkManager::QueueDequipCommand put message in overrun state!\n");
    }

    cmd_count++;
}

void NetworkManager::QueueDigCommand(iCelEntity *entity, csString resource)
{
    if ( outbound->msg->current > ( outbound->msg->bytes->GetSize() - 100 ) )
    {
        CPrintf(CON_DEBUG, "Sent all commands [%d] due to possible Message overrun.\n", cmd_count );
        SendAllCommands();
    }

    outbound->msg->Add( (int8_t) psNPCCommandsMessage::CMD_DIG);
    outbound->msg->Add( (uint32_t) entity->GetID() );
    outbound->msg->Add( resource );

    if ( outbound->msg->overrun )
    {
        CS_ASSERT(!"NetworkManager::QueueDigCommand put message in overrun state!\n");
    }

    cmd_count++;
}

void NetworkManager::QueueDropCommand(iCelEntity *entity, csString slot)
{
    if ( outbound->msg->current > ( outbound->msg->bytes->GetSize() - 100 ) )
    {
        CPrintf(CON_DEBUG, "Sent all commands [%d] due to possible Message overrun.\n", cmd_count );
        SendAllCommands();
    }

    outbound->msg->Add( (int8_t) psNPCCommandsMessage::CMD_DROP);
    outbound->msg->Add( (uint32_t) entity->GetID() );
    outbound->msg->Add( slot );

    if ( outbound->msg->overrun )
    {
        CS_ASSERT(!"NetworkManager::QueueDropCommand put message in overrun state!\n");
    }

    cmd_count++;
}

void NetworkManager::SendAllCommands()
{
    if (cmd_count)
    {
        outbound->msg->Add( (int8_t) psNPCCommandsMessage::CMD_TERMINATOR);
        outbound->msg->ClipToCurrentSize();

        msghandler->SendMessage(outbound->msg);

        delete outbound;
        PrepareMessage();
    }
}


void NetworkManager::ReAuthenticate()
{
    Authenticate(host,port,user,password);
}

void NetworkManager::ReConnect()
{
    if (!connection->Connect(host,port))
    {
        CPrintf(CON_ERROR, "Couldn't connect to %s on port %d.\n",(const char *)host,port);
        return;
    }
    // 2 seconds to allow linkdead messages to be processed
    psNPCReconnect *recon = new psNPCReconnect(2000, this, true);
    npcclient->GetEventMgr()->Push(recon);
}

void NetworkManager::SendConsoleCommand(const char *cmd)
{
    psServerCommandMessage msg(0,cmd);
    msg.SendMessage();
}

/*------------------------------------------------------------------*/

psNPCReconnect::psNPCReconnect(int offsetticks, NetworkManager *mgr, bool authent)
: psGameEvent(0,offsetticks,"psNPCReconnect"), authent(authent), networkMgr(mgr)
{

}

void psNPCReconnect::Trigger()
{
    if(!running)
        return;
    if(!authent)
    {
        networkMgr->ReConnect();
        return;
    }
    if(!networkMgr->IsReady())
    {
        CPrintf(CON_DEBUG, "Reconnecting...\n");
        networkMgr->ReAuthenticate();
    }
    networkMgr->reconnect = false;
}
