/******************************************************************
  
  Module:  aigoal.h
  
  Author: Borut Pfeifer
  
  Copyright 2005 Sony Online Entertainment.  All rights reserved.
  
*******************************************************************/
#ifndef AIGOAL_H
#define AIGOAL_H
//-------------------------------------------------------- Includes

#include "..\ai.h"
#include "..\gameobj.h"
#include "aiblackboard.h"

//---------------------------------------------- Class Declarations
class cIntelNPC;
class cGameObjectRegistry;

typedef enum
{
  GOAL_ATTACK,
  GOAL_GOTO,
  GOAL_PATROL,
  GOAL_FADEAWAY,
  GOAL_STAND,
  GOAL_RANGED,
  GOAL_FLEE,
  GOAL_WANDER,
  GOAL_FOLLOW,
  GOAL_HUNT,
  GOAL_CAST,
  GOAL_DEFEND,
  GOAL_PLAYANIM,
} eGoalType;

class cAiGoal;
class cAi;
class cAnimCharControllerInterface;

class cGoalQueue
{
public:
  cGoalQueue();
  ~cGoalQueue();

  void     Init(cAi* pOwner);
  void     Push(int priority, cAiGoal *pNewGoal);
  void     Pop(cAiGoal *pGoal);
  void     ChangePriority(cAiGoal *pGoal, int priority);
  cAiGoal* FindGoalType(eGoalType type);

  void     Update(float time);
  void     Clear();

  int GetHighestPriority();

private:
  void   Insert(cAiGoal *pGoal, int index);
  void   Remove(cAiGoal *pGoal);

  cAi*                    mpOwner;
  SyVector<cAiGoal*>      mGoals;
  cAiGoal*                mpCurGoal;
};

class cAiLOSMemory
{
public:
  cAiLOSMemory();
  ~cAiLOSMemory();

  tGameObjectID           mTargetID;
  float                   mLastSeenTargetTime;
  SyVect3                 mLastSeenTargetPos;
  int                     mTargetLOSTicket;
  bool                    mbHasLOS;
  bool                    mbHasSeenTarget;
  bool                    mbIsTargetInvisible;
  bool                    mbIsTouchingInvisibleTarget;

  bool                    mbUseTargetPos;
  SyVect3                 mTargetPos;

  void            Update(cGameObject* pOwner, float time);
  void            SetTargetPosition(const SyVect3& targetPos);
  void            SetTarget(tGameObjectID target);
  void            Clear();
  cGameObject*    GetTarget(cGameObject* pOwner);
  bool            CanSeeTarget(); // includes invisibility test
  bool            HasLOS();
  bool            GetLastSeenTargetPos(SyVect3& pos);
  bool            IsLOSPending();
  void            CancelLOSRequest();
};


class cAi : public cAiInterface
{
public:
  cAi();
  virtual ~cAi();

  void                    Init(cGameObject *owner);

  void                    FindTarget();

  virtual  void           Update(float time);
  virtual cGameObject *   GetOwner(){return mpOwner;};

  virtual void            AttackTarget(int priority);
  virtual void            GoTo(int priority,const SyVect3 &destination);
  virtual void            FadeAway(int priority);  // maybe BurnOut would be better?
  virtual void            Stand(int priority);  // maybe BurnOut would be better?
  virtual void            Flee(int priority, tGameObjectID targetID, float time = 0.0f, float goalDistance = 5.0f); 
  virtual void            Flee(int priority, const SyVect3& pos, float time = 0.0f, float goalDistance = 5.0f); 
  virtual void            Wander(int priority, float time = 0.0f); 
  virtual void            Follow(int priority, tGameObjectID targetID);  
  virtual void            Defend(int priority, tGameObjectID targetID);  
  virtual void            Hunt(int priority, tGameObjectID targetID, const SyVect3& startLoc); 
  virtual void            PatrolAddWaypoint(const SyVect3& markerLoc, float speed, float pauseTime); 
  virtual void            PatrolClearWaypoints(); 
  virtual void            PatrolSetLooping(bool bLoop); 
  
  virtual void            Reset();

  virtual void            SetAttackTarget(tGameObjectID target);
  virtual cGameObject*    GetAttackTarget();
  virtual bool            CanSeeAttackTarget();
  virtual bool            HasLOSToAttackTarget();
  virtual bool            GetLastSeenAttackTargetPos(SyVect3& pos);

  virtual void            SetDefendTarget(tGameObjectID target);
  virtual cGameObject*    GetDefendTarget();
  virtual bool            CanSeeDefendTarget();
  virtual bool            GetLastSeenDefendTargetPos(SyVect3& pos);
  bool                    IsOutsideDefendRadius();

  virtual void            OnAttacked(tGameObjectID attacker,int damage);

  void                    Pop(cAiGoal *goal);
  void                    ChangePriority(cAiGoal *pGoal, int priority);

  bool                    TestMaxClosestAttackers(cAIBlackboard::RecordType type,
                                                  int maxAttackers,
                                                  tGameObjectID targetID,
                                                  float maxDist);
  bool                    IsOnCamera();
  bool                    IsPointInFOV(const SyVect3& point);
  bool                    IsRangedAttacker();
  bool                    IsMeleeAttacker();

  void                    CalculateFleeDirection(cGameObject* pTarget, SyVect3& fleeDir);

  static void         RegisterTuningVariables();

  float GetActivationRadius();
  float GetDefendRadius();
protected:
  // tuning vars
  static float smDefendRadius;
  static float smActivationRadius;


  void                    CheckForIncomingProjectiles();
  void                    CheckForNegativeAreaEffects();

  cGameObject *           mpOwner;
  cGoalQueue              mGoalQueue;

  cAiLOSMemory            mAttackTarget;
  cAiLOSMemory            mDefendTarget;

  tGameObjectID           mLastDodgedProjectileID;
};                   


class cAiGoal
{
public:
  cAiGoal();
  virtual ~cAiGoal();

  void Init(cAi *owner,int priority);
  void SetPriority(int priority);

  virtual void Enter(){};
  virtual void Update(float time){};  // called only on active goal
  virtual void Exit(){};

  int          GetPriority(){return mPriority;};
  virtual eGoalType GetType()=0;

protected:

  cGameObject*          GetGameObject();
  cIntelNPC*            GetIntel();
  cGameObjectRegistry*  GetRegistry();
  cAnimCharControllerInterface* GetAnimInterface();

  cAi*           mpOwner;
  int            mPriority;

private:
  cAiGoal(const cAiGoal&) {};
};


inline cGameObject* cAiGoal::GetGameObject()
{
  return mpOwner->GetOwner();
}

inline cGameObjectRegistry* cAiGoal::GetRegistry()
{
  return GetGameObject()->GetRegistry();
}

template <class T>
class MemoryQueue
{
public:
  MemoryQueue(int max); 
  virtual ~MemoryQueue();

  void Add(const T& elem);

  int Begin() { return mQueue.Begin(); }
  int End() { return mQueue.End(); }
  int Size() { return mQueue.Size(); }
  T& operator()(int i) { return mQueue(i); }
  const T& operator()(int i) const { return mQueue(i); }

protected:
  SyVector<T> mQueue;
  int mMaxElements;
};

template <class T>
MemoryQueue<T>::MemoryQueue(int max)
: mMaxElements(max)
{
  mQueue.Clear();
}

template <class T>
MemoryQueue<T>::~MemoryQueue()
{

}

template <class T>
void MemoryQueue<T>::Add(const T& elem)
{
  for (int i=mQueue.Begin(); i != mQueue.End(); i = mQueue.Next(i))
  {
    if (elem.Approx(mQueue(i)))
    {
      // found something similar
      return;
    }
  }

  if (mQueue.Size() > 0)
  {
    if (mQueue.Size() >= mMaxElements)
    {
      mQueue.Erase();
    }

    mQueue.Add(elem);

    int beginElem = mQueue.Begin();
    int prev;

    for (int i=mQueue.Prev(mQueue.End()); i > mQueue.Begin(); i = mQueue.Prev(i))
    {
      prev = mQueue.Prev(i);

      if (prev >= beginElem)
      {
        mQueue(i) = mQueue(prev);
      }
    }

    mQueue(0) = elem;
  }
  else
  {
    mQueue.Add(elem);
  }
}

class cAIWanderSteering
{
public:
  cAIWanderSteering();

  void Init(cGameObject* pNPC, float baseRate, float randAdjust);

  void MoveNPC(cGameObject* pNPC, float speed, float radius);

  void SetHPR(const SyVect3& hpr);

private:
  SyVect3 mWanderHPR;
  float mWanderRate;
  float mLastWanderAdjust;
};


#endif
