/*
* npcbehave.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.
*
*/

/* This file holds definitions for ALL global variables in the planeshift
* server, normally you should move global variables into the psServer class
*/
#ifndef __NPCBEHAVE_H__
#define __NPCBEHAVE_H__

#include <csgeom/matrix3.h>

#include <util/prb.h>
#include <csutil/array.h>
#include <iutil/document.h>
#include "util/eventmanager.h"
#include "util/consoleout.h"
#include "perceptions.h"
#include "walkpoly.h"

class ScriptOperation;
class Perception;
class Reaction;
class Behavior;
class NPC;
class EventManager;
class BehaviorSet;
struct iCelEntity;
struct iSector;
class csPath;

/**
* This is just a collection of behaviors for a particular
* type of NPC.
*/
class BehaviorSet
{
protected:
    // BinaryTree<Behavior> behaviors;
    csArray<Behavior*> behaviors;
    Behavior *active;
    float max_need;

public:
    BehaviorSet() { active=NULL; }

    void Add(Behavior *b);
    Behavior *Find(Behavior *key);
    
    Behavior *Find(const char *name);
    void DeepCopy(BehaviorSet& other);
    void ClearState();

    void Advance(csTicks delta,NPC *npc,EventManager *eventmgr);
    void Interrupt(NPC *npc,EventManager *eventmgr);
    void ResumeScript(NPC *npc,EventManager *eventmgr,Behavior *which);
    Behavior *GetCurrentBehavior() { return active; }
    void DumpBehaviorList();
};


/**
* A collection of behaviors and reactions will represent a type of
* npc.  Each npc will be assigned one of these types.  This lets us
* reuse the same script for many mobs at once--each one keeping its
* own state information.
*/
class NPCType
{
protected:
    csArray<Reaction*> reactions;
    BehaviorSet behaviors;
    csString name;
    float    ang_vel,vel;

public:
    NPCType();
    NPCType(const char *n) { name=n; ang_vel=vel=999; }
    NPCType(NPCType& other) { DeepCopy(other); }

    ~NPCType();
    void DeepCopy(NPCType& other);
    void ClearState();

    bool Load(iDocumentNode *node);
    csString GetName(){ return name; }

    void Advance(csTicks delta,NPC *npc,EventManager *eventmgr);
    void Interrupt(NPC *npc,EventManager *eventmgr);
    void ResumeScript(NPC *npc,EventManager *eventmgr,Behavior *which);
    void FirePerception(NPC *npc,EventManager *eventmgr,Perception *pcpt);

    void DumpBehaviorList() { behaviors.DumpBehaviorList(); }

    Behavior *GetCurrentBehavior()
    {
        return behaviors.GetCurrentBehavior();
    }
    bool operator==(NPCType& other) const
    {
        return (name == other.name);
    }
    bool operator<(NPCType& other) const
    {
        return (strcmp(name,other.name)<0);
    }
    float GetAngularVelocity()
    {
        if (ang_vel != 999)
            return ang_vel;
        else
            return 90*TWO_PI/360;
    }
    float GetVelocity()
    {
        if (vel != 999)
            return vel;
        else
            return 1.5;
    }
};


/**
* A Behavior is a general activity of an NPC.  Guarding,
* smithing, talking to a player, fighting, fleeing, eating,
* sleeping are all behaviors I expect to be scripted
* for our npcs.
*/
class Behavior
{
protected:
    csArray<ScriptOperation*> sequence;
    size_t  current_step;
    bool loop,is_active;
    float need_decay_rate;  // need lessens while performing behavior
    float need_growth_rate; // need grows while not performing behavior
    float completion_decay; // need lessens AFTER behavior script is run once
    float init_need;        // starting need, also used in ClearState resets
    csString name;

    float current_need;
    float new_need;
    csTicks last_check;

public:
    Behavior();
    Behavior(const char *n) { name = n; }
    Behavior(Behavior& other) { DeepCopy(other); }

    const char *GetName() { return name; }

    void DeepCopy(Behavior& other);
    bool Load(iDocumentNode *node);

    bool LoadScript(iDocumentNode *node,bool top_level=true);

    void  Advance(csTicks delta,NPC *npc,EventManager *eventmgr);
    float CurrentNeed()
    { return current_need; }
    float NewNeed()
    { return new_need; }
    void  CommitAdvance()
    {
        if (new_need!=-999)
            current_need = new_need;
    }
    void ApplyNeedDelta(float delta_desire)
    { new_need += delta_desire; }
    void SetActive(bool flag)
    { is_active = flag; }
    bool GetActive()
    { return is_active; }
    void SetCurrentStep(int step) { current_step = step; }
    void SetCompletionDecay(float decay)
    {   CPrintf(CON_DEBUG, "Setting completion decay to %1.1f.\n", decay); completion_decay = decay; }
    void ResetNeed() { current_need = new_need = init_need; }

    void StartScript(NPC *npc,EventManager *eventmgr);
    void RunScript(NPC *npc,EventManager *eventmgr);
    void ResumeScript(NPC *npc,EventManager *eventmgr);
    void InterruptScript(NPC *npc,EventManager *eventmgr);
    
    inline bool operator==(const Behavior& other)
    {    return (current_need == other.current_need &&
                name == other.name  );    }
    bool operator<(Behavior& other)
    {
        if (current_need > other.current_need)
            return true;
        if (current_need < other.current_need)
            return false;
        if (strcmp(name,other.name)>0)
            return true;
        return false;
    }
};


/**
* This is the base class for all operations in action scripts.
* We will use these to set animation actions, move and rotate
* the mob, attack and so forth.  These should be mostly visual
* or physical things, except for the ability to create new
* perceptions, which can kick off other actions.
*/
class ScriptOperation
{
protected:
    float     vel;  // Shared by many functions
    bool      completed; /// This flag is set to false by Run(), and set to true by CompleteOperation(), in case of multiple Complete's being called.

    /// This function is used by MoveTo AND Navigate operations
    int StartMoveTo(NPC *npc,EventManager *eventmgr,csVector3& dest, iSector* sector, float vel,const char *action, bool autoresume=true);

    void TurnTo(NPC *npc,csVector3& dest, iSector* destsect,csVector3& forward);

public:

    virtual bool Run(NPC *npc,EventManager *eventmgr)=0;
    virtual void Advance(float timedelta,NPC *npc,EventManager *eventmgr);
    virtual void InterruptOperation(NPC *npc,EventManager *eventmgr)      { }
    virtual bool CompleteOperation(NPC *npc,EventManager *eventmgr) { return true; }
    virtual bool Load(iDocumentNode *node)=0;
    virtual ScriptOperation *MakeCopy()=0;

    float GetVelocity(NPC *npc);
    void AddRandomRange(csVector3& dest,float radius);

    static csRandomGen rng;
};

class LocationType;

/**
* Moving entails a velocity vector and an animation action.
*/
class MoveOperation : public ScriptOperation
{
protected:
    csString  action;
    bool inside_rgn;
    int consec_collisions;
    csVector3 current_pos, last_checked_pos;
    float duration;

public:

    MoveOperation() { consec_collisions=0; vel=0; inside_rgn = true; duration = 0;}
    virtual ~MoveOperation() { }
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();

    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual void Advance(float timedelta,NPC *npc,EventManager *eventmgr);
    virtual void InterruptOperation(NPC *npc,EventManager *eventmgr);
    virtual bool CompleteOperation(NPC *npc,EventManager *eventmgr);

};

/**
* MovePath specifies the name of a path and an animation action.
*/
class MovePathOperation : public ScriptOperation
{
protected:
    csString  action;
    csString  pathname;
    csPath   *path;

public:

    MovePathOperation() { path=NULL; }
    virtual ~MovePathOperation() { }
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();

    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual void Advance(float timedelta,NPC *npc,EventManager *eventmgr);
    virtual void InterruptOperation(NPC *npc,EventManager *eventmgr);
};

/**
* Moving to a spot entails a position vector, a linear velocity,
* and an animation action.
*/
class MoveToOperation : public ScriptOperation
{
protected:
    csVector3 dest;
    csVector3 localDest;
    csString  action;
    psAPath   path;
public:
    MoveToOperation() { dest.Set(0,0,0); vel=0; }
    virtual ~MoveToOperation() { }

    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual void Advance(float timedelta,NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
    virtual bool CompleteOperation(NPC *npc,EventManager *eventmgr);
};

/**
* Rotating requires storing or determining the angle to 
* rotate to, and the animation action.
*/
class RotateOperation : public ScriptOperation
{
protected:
    enum
    {   
        ROT_UNKNOWN,
        ROT_ABSOLUTE,
        ROT_TARGET,
        ROT_LOCATEDEST,
        ROT_RANDOM,
        ROT_REGION
    };
    float     min_range, max_range;
    float     rot_angle,target_angle,angle_delta;
    float     ang_vel;
    int          op_type;
    csString  action;

    float GetAngularVelocity(NPC *npc);

public:

    RotateOperation() { vel=0; op_type=ROT_UNKNOWN; ang_vel=999; }
    virtual ~RotateOperation() { }
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();

    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual void InterruptOperation(NPC *npc,EventManager *eventmgr);
    virtual bool CompleteOperation(NPC *npc,EventManager *eventmgr);
    float SeekAngle(NPC* npc, float targetYRot);           // Finds an angle which won't lead to a collision
};


/**
* Locate is a very powerful function which will find
* the nearest object of the named tag, within a range.
*/
class LocateOperation : public ScriptOperation
{
protected:
    float    range;
    csString object;
    bool     static_loc;
    bool     located;
    csVector3 located_pos;
    float     located_angle;
    iSector*  located_sector;
    bool     random;

public:

    LocateOperation() { range = 0; static_loc=true; located=false; }
    virtual ~LocateOperation() { }

    /// Use -1 for located_range if range scheck sould be skipped.
    void CalculateWaypointList(NPC *npc, csVector3 start_pos, iSector* start_sector,
                               csVector3 located_pos, iSector* located_sector, float located_range);

    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
};

/**
* Navigate moves the NPC to the position and orientation
* of the last located thing.  (See LocateOperation)
*/
class NavigateOperation : public ScriptOperation
{
protected:
    csString action;

public:

    NavigateOperation() { vel=0; };
    virtual ~NavigateOperation() {};

    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual void Advance(float timedelta,NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
    virtual bool CompleteOperation(NPC *npc,EventManager *eventmgr);
};

/**
* Chase updates periodically and turns, moving towards a certain
* location.  This is normally used to chase a targeted player.
*/
class ChaseOperation : public ScriptOperation
{
protected:
    csString  action;
    int       type;
    float     range;
    csVector3 offset;
    uint32_t  target_id;
    psAPath   path;
    csVector3 localDest;
    bool      completed;
    
    enum
    {
        UNKNOWN,NEAREST,OWNER,TARGET
    };
public:

    ChaseOperation() { target_id=(uint32_t)-1; type = UNKNOWN; range=0; vel=0; };
    virtual ~ChaseOperation() {};

    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual void Advance(float timedelta,NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
    virtual bool CompleteOperation(NPC *npc,EventManager *eventmgr);
    virtual void InterruptOperation(NPC *npc,EventManager *eventmgr);
};

class Waypoint;

/**
 * Wander auto-navigates randomly between a network of waypoints specified
 * in npcdefs.xml.
 */
class WanderOperation : public ScriptOperation
{
protected:
    csString  action;
    Waypoint *active_wp,*prior_wp;
    csVector3 dest,current_pos;
    float     height_delta_per_sec;  /// used for interpolation of y coord between waypoints
    bool      random;

    /** Calculate a random position within the waypoint as destination */
    void CalculateTargetPos(csVector3& dest);
    
    /** From current waypoint find next */
    bool FindNextWaypoint(NPC *npc);
    
    /** Set up move from current position to next waypoint */
    void StartMoveToWaypoint(NPC *npc,EventManager *eventmgr);
    
public:

    WanderOperation() { active_wp=NULL; prior_wp=NULL; height_delta_per_sec=0.0f;};
    virtual ~WanderOperation() {};

    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual void Advance(float timedelta,NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
    virtual bool CompleteOperation(NPC *npc,EventManager *eventmgr);
};

/**
* Melee will tell the npc to attack the most hated
* entity within range.
*/
class MeleeOperation : public ScriptOperation
{
protected:
    float seek_range, melee_range;
    iCelEntity *attacked_ent;
    bool  completed;
public:

    MeleeOperation() { attacked_ent=NULL; seek_range=0; melee_range=0; }
    virtual ~MeleeOperation() {}

    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();

    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual void Advance(float timedelta,NPC *npc,EventManager *eventmgr);
    virtual void InterruptOperation(NPC *npc,EventManager *eventmgr);
    virtual bool CompleteOperation(NPC *npc,EventManager *eventmgr);
};

/**
* Pickup will tell the npc to pickup a nearby
* entity (or fake it).
*/
class PickupOperation : public ScriptOperation
{
protected:
    csString object;
    csString slot;
    int      count; // Number of items to pick up from a stack
    
public:

    PickupOperation() {};
    virtual ~PickupOperation() {};
    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
};

/**
* Equip will tell the npc to equip a item
*/
class EquipOperation : public ScriptOperation
{
protected:
    csString item;
    csString slot;
    int      count; // Number of items to pick up from a stack
    
public:

    EquipOperation() {};
    virtual ~EquipOperation() {};
    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
};

/**
* Dequip will tell the npc to dequip a item
*/
class DequipOperation : public ScriptOperation
{
protected:
    csString slot;
    
public:

    DequipOperation() {};
    virtual ~DequipOperation() {};
    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
};

/**
* Talk will tell the npc to communicate to a nearby
* entity.
*/
class TalkOperation : public ScriptOperation
{
protected:
    csString text;
    csString command;
    bool target;

public:

    TalkOperation() {};
    virtual ~TalkOperation() {};
    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
};

/**
* Visible will make the npc visible.
*/
class VisibleOperation : public ScriptOperation
{
public:

    VisibleOperation() {};
    virtual ~VisibleOperation() {};
    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
};

/**
* Invisible will make the npc invisible.
*/
class InvisibleOperation : public ScriptOperation
{
public:

    InvisibleOperation() {};
    virtual ~InvisibleOperation() {};
    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
};

/**
* Reproduce will make the npc to setup a spawn point here
*/
class ReproduceOperation : public ScriptOperation
{
protected:

public:

    ReproduceOperation() {};
    virtual ~ReproduceOperation() {};
    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
};

/**
* BeginLoop will only print BeginLoop for debug purpose.
* Looping will be done by the EndLoopOperation.
*/
class BeginLoopOperation : public ScriptOperation
{
public:
    int iterations;

public:

    BeginLoopOperation() { iterations=0; }
    virtual ~BeginLoopOperation() { }

    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
};

/**
* EndLoop will jump back to the beginning
* of the loop.
*/
class EndLoopOperation : public ScriptOperation
{
protected:
    int loopback_op;
    int current;
    int iterations;

public:

    EndLoopOperation(int which,int iterations) { loopback_op = which; this->iterations = iterations; current = 0;}
    virtual ~EndLoopOperation() { }
    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
};

/**
* Wait will simply set the mesh animation to
* something and sit there for the desired number
* of seconds.
*/
class WaitOperation : public ScriptOperation
{
protected:
    float duration;
    csString action;

public:

    WaitOperation() { duration=0; }
    virtual ~WaitOperation() { }

    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual void Advance(float timedelta,NPC *npc,EventManager *eventmgr);
    virtual ScriptOperation *MakeCopy();
};

/**
* Drop will make the NPC drop whatever he is
* holding.
*/
class DropOperation : public ScriptOperation
{
protected:
    csString slot;

public:

    DropOperation() {};
    virtual ~DropOperation() {};
    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
};

/**
* Dig will make the NPC dig for a resource
*/
class DigOperation : public ScriptOperation
{
protected:
    csString resource;

public:

    DigOperation() {};
    virtual ~DigOperation() {};
    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
};

/**
* Debug will turn on and off debug for the npc. Used for debuging
*/
class DebugOperation : public ScriptOperation
{
protected:
    bool     state;
    csString exclusive;

public:

    DebugOperation() {};
    virtual ~DebugOperation() {};
    virtual bool Run(NPC *npc,EventManager *eventmgr);
    virtual bool Load(iDocumentNode *node);
    virtual ScriptOperation *MakeCopy();
};



class psResumeScriptEvent : public psGameEvent
{
protected:
    NPC *npc;
    EventManager *eventmgr;
    Behavior     *behavior;

public:
    psResumeScriptEvent(int offsetticks, NPC *c,EventManager *mgr,Behavior *which);
    virtual void Trigger();  // Abstract event processing function
};

struct iCelEntity;

class psGameObject
{
public:
    static float Matrix2YRot(const csMatrix3& mat);
    static float GetAngle(float x, float y);

    static void GetPosition(iCelEntity *entity, csVector3& pos, float& yrot,iSector*& sector);
    static void SetPosition(iCelEntity *entity, csVector3& pos, iSector* = NULL);
    static void SetRotationAngle(iCelEntity *entity, float angle);
    static void GetRotationAngle(iCelEntity *entity, float& yrot)
    {
        csVector3 pos;
        iSector *sector;
        GetPosition(entity,pos,yrot,sector);
    }
    static float CalculateIncidentAngle(csVector3& pos, csVector3& dest);
    static float Distance(csVector3& pos, iSector* sector, csVector3& dest, iSector* sector2);
    static void ClampRadians(float &target_angle);

};


#endif

