/*
 * npcclient.h
 *
 * 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.
 *
 */
#ifndef __NPCCLIENT_H__
#define __NPCCLIENT_H__

#include <csutil/csstring.h>
#include <csutil/hash.h>
#include <csutil/randomgen.h>
#include <csutil/ref.h>
#include <csutil/list.h>
#include "net/pstypes.h"

#include "engine/celbase.h"

#include "util/prb.h"
#include "util/growarray.h"
#include "util/psconst.h"
#include "util/serverconsole.h"

struct iObjectRegistry;
struct iDocumentNode;
struct iConfigManager;
struct iCollideSystem;

class  psDatabase;
class  MsgHandler;
class  psNetConnection;
class  EventManager;
class  NetworkManager;
class  NPCType;
class  NPC;
class  gemNPCActor;
class  gemNPCObject;
struct iCelEntity;
struct iVFS;
class  Perception;
class  PathManager;
class  psWorld;
class  Location;
class  LocationType;
class  Waypoint;
class  psPFMaps;
class  psTribe;

/**
 * The main NPC Client class holding references to important superclient objects
 */
class psNPCClient : public CelBase, public iCommandCatcher
{
public:
    psNPCClient();
    /**
     * Cleans up all allocated memory and removes all the players from the world.
     */
    ~psNPCClient();

    /**
     * Initialize the superclient.
     * Starts a thread for the Network, Message Handler. Also 
     * binds the status socket for the network.
     */
    bool Initialize(iObjectRegistry* object_reg,const char *host, const char *user, const char *pass, int port);

    /**
     * Just make iObjectRegistry available to other classes.
     */
    iObjectRegistry *GetObjectReg()
    { return objreg; }

    /** Returns a random number.
     *
     * @return Returns a random number between 0.0 and 1.0.
     */
    float GetRandom() { return randomGen.Get(); }
    /** Returns a random number with a limit.
     *
     * @return Returns a random number between 0 and limit.
     */
    uint32 GetRandom(uint32 limit) { return randomGen.Get(limit); }
    
    /**
     * Load and starts an optional script file that sets 
     * up the server. After that it jumps into the
     * server console's main loop, accepting commands from the user.
     */
    int MainLoop ();

    virtual void CatchCommand(const char *cmd);

    /**
     * If a world is ready and loaded this returns true.
     */
    bool IsReady();
    
    /**
    * This does 1 AI calc per NPC, 1 AI calc per Tribe then returns.
    */
    void Tick();
   
    /**
     * Load a map into a region.  This is a copy of code
     * from entitymanager->CreateRoom().
     */
    bool LoadMap(const char *mapfile);
   
    iCelEntity* FindActor( const char* name );

    /**
     * This function handles the searching for the specified region name
     * so that other functions can refer to the region directly.
     */
    LocationType *FindRegion(const char *regname);

    /**
     * This function handles the searching for the specified object
     * type and basically does the work for the <locate> script command.
     */
    Location *FindNearestLocation(const char *loctype, csVector3& pos, iSector* sector, float range = -1, float *found_range = NULL);

    /**
     * This function handles the searching for the specified object
     * type and basically does the work for the <locate> script command.
     */
    Location *FindRandomLocation(const char *loctype, csVector3& pos, iSector* sector, float range = -1, float *found_range = NULL);

    /**
     * This iterates over all waypoints and finds the nearest one.
     * There is probably a more efficient structure to accomplish this.
     */
    Waypoint *FindNearestWaypoint(csVector3& v, iSector* sector, float range = -1, float * found_range = NULL);

    /**
     * This iterates over all waypoints and finds the nearest one.
     * There is probably a more efficient structure to accomplish this.
     */
    Waypoint *FindRandomWaypoint(csVector3& v, iSector* sector, float range = -1, float * found_range = NULL);
    
    /**
     * This iterates over all waypoints and finds the one with the given id.
     * There is probably a more efficient structure to accomplish this.
     */
    Waypoint *FindWaypoint(int id);

    /**
     * Find the shortest path between waypoint start and stop.
     */
    csList<Waypoint*> FindWaypointPath(Waypoint * start, Waypoint * end);
    
    /**
     * This function handles the searching for the specified NPCType name
     * so that other NPCTypes can subclass an existing one.
     */
    NPCType *FindNPCType(const char *npctype_name);

    /**
     * Returns the network handler so script ops can invoke the network.
     */
    NetworkManager *GetNetworkMgr()
    { return network; }

    /**
     * Returns the network handler so script ops can invoke the network.
     */
    psNetConnection *GetNetConnection()
    { return connection; }

    /**
     * Sends a perception to a specified NPC, or broadcasts to all npcs, or to all within a range of a position.
     *
     * @param npc The npc to percept or NULL it all NPCs is to be percepted. If NPC is null max_range
                  can be set to null to broadast to all or both
     *            base_pos and base_sector has to be set to broadcast only to items within the given range.
     */
    void TriggerEvent(NPC *npc,Perception *pcpt,float max_range=0,csVector3 *base_pos=NULL, iSector *base_sector=NULL);

    EventManager *GetEventMgr()
    { return eventmanager; }

    /**
     * Disconnect from server nicely, before quitting.
     */
    void Disconnect();

    /**
     * SetEntityPos finds the given ID entity, and updates
     * its position in mesh and linmove.
     */
    void SetEntityPos(PS_ID id, csVector3& pos, iSector* sector);

    /**
     * Find the NPC* attached to the entity with the specified ID
     */
    NPC *FindNPC(PS_ID entid);
    NPC *FindNPC(const char *name);

    /**
     * Get the spline path list.
     */
    PathManager *GetPathManager()
    { return pathmgr; }

    /**
     * List all NPCs matching pattern to console.
     */
    void ListAllNPCs(char *pattern);

    /**
     * Find one npc and print its current state info.
     */
    bool DumpNPC(char *pattern);
    
    /**
     * List all known entities on superclient.
     */
    void ListAllEntities(bool onlyCharacters = false);

    /**
     * List all tribes matching pattern to console.
     */
    void ListTribes(char *pattern);
    
    /**
     * List all waypoints matching pattern to console.
     */
    void ListWaypoints(char *pattern);
    
    /**
     * List all locations matching pattern to console.
     */
    void ListLocations(char *pattern);
    
    /**
     * Special handling for death notifications from the server
     * is required, to stop behavior scripting, delete hate lists,
     * etc.
     */
    void HandleDeath(NPC *who);

    /**
     * This is called when a human player dies, enabling NPCs
     * to find out and react accordingly.
     */
    void HandlePlayerDeath(PS_ID who);

    /**
     * This function removes any associations if the entity to
     * be removed is an NPC managed by this superclient, then
     * calls the normal CelBase RemoveItem function.
     */
    //bool RemoveItem(iCelEntity *item);

    void Add( gemNPCObject* object );
    void Remove ( unsigned id );
    void RemoveAll();
    gemNPCObject *FindCharacterID(int char_id);
    gemNPCObject *FindEntityID(unsigned entityID);

    void AttachNPC( gemNPCActor* actor, uint8_t DRcounter ); 
    iCelEntity* FindEntity( uint32_t id );

    iCollideSystem *GetCollDetSys() { return cdsys; }
    NPC* FindAttachedNPC(iCelEntity *entity);

    bool ReadSingleNPC(int char_id);
    NPC *FindNPC(int character_id);
    
    psPFMaps * GetMaps() { return PFMaps; }

    psWorld * GetWorld() { return world; }

    iEngine * GetEngine() { return engine; }
    

protected:

    bool ReadNPCsFromDatabase();

    bool LoadNPCTypes(const char *xmlfile,iVFS *vfs);
    bool LoadNPCDefinitions(const char *xmlfile,iVFS *vfs);

    bool LoadLocations(iDocumentNode *topNode);
    bool LoadWaypoints(iDocumentNode *topNode);

    bool LoadLocations();
    bool LoadWaypoints();
    
    /** Load Tribes from db */
    bool LoadTribes();

    // Fns to get NPC* from iCelEntity*   
    void AttachNPC(iCelEntity *entity, NPC *npc);
    void UnattachNPC(iCelEntity *entity, NPC *npc);

    /**
     * Find all items that are close to NPC's and percept the
     * NPC.
     */
    void PerceptProximityItems();
    
    psNetConnection           *connection;
    iObjectRegistry*           objreg;
    csRef<iEngine>             engine;
    csRef<iConfigManager >     configmanager;
    csRef<MsgHandler>          msghandler;
    EventManager              *eventmanager;
    NetworkManager            *network;
    PathManager               *pathmgr;
    psDatabase                *database;

    BinaryRBTree<NPCType>      npctypes;
    BinaryRBTree<LocationType> loctypes;
    BinaryRBTree<Waypoint>     waypoints;
    csArray<NPC*>              npcs;
    csArray<psTribe*>          tribes;
    csHash<iCelEntity*>        npc_entities;
    csArray<gemNPCObject*>     objects;

    csRef<iCollideSystem>      cdsys;

    psWorld                   *world;
    
    psPFMaps                  *PFMaps;

    /// Counter used to start events at every nth client tick
    unsigned int               tick_counter;

    csRandomGen                randomGen;
};


#endif

