#ifndef BOT_UTIL_H
#define BOT_UTIL_H

#include "bot_types.h"

// Misc util functions
void UTIL_DrawBeam(CBaseEntity *pEntity, Vector start, Vector end, int width,
        int noise, int red, int green, int blue, int brightness, int speed, int life = 4);
void UTIL_DrawBeamToAll(Vector start, Vector end, int width, int noise, int red, int green, int blue, 
						int brightness, int speed);
Vector WrapAngles (Vector angles_to_wrap);
float UTIL_WrapAngle (float angle_to_wrap);
void UTIL_Printf(const char *msg, int type = HUD_PRINTNOTIFY, edict_t *pEntity = NULL);
void UTIL_PrintEntity (edict_t *pEntity, const char *szFileName = NULL);
CBasePlayer *FindPlayerByName(const char *pName);
void TelePortPlayerToPlayer(edict_t *pPlayer1, edict_t *pPlayer2);
inline bool UTIL_IsBot(CBasePlayer *pPlayer) { return ((!pPlayer->IsNetClient())); };// && (pPlayer->pev->flags & FL_FAKECLIENT)); };
bool IsNull(edict_t *pEdict);
bool IsNull(CBaseEntity *pEnt);
bool UTIL_IsValidPlayer(CBasePlayer *pPlayer, bool CheckAlive = true, bool CheckObserver = true);
int UTIL_EntDir(CBaseEntity *pEntity, Vector v_vec);
Vector PredictEntityPos(CBaseEntity *pEntity, float Time, bool UseTraceLine = true);
bool IsAttack(edict_t *pent);
bool IsBeamAttack(edict_t *pent);
bool IsNonBeamAttack(edict_t *pent);
bool IsKiBall(edict_t *pent);
bool IsOtherAttack(edict_t *pent);
short UTIL_GetHeadingPercentage(CBaseEntity *pEnt1, CBaseEntity *pEnt2);
bool EntityHeadsToPlayer(CBasePlayer *pPlayer, CBaseEntity *pEntity);
inline float UTIL_Distance(Vector vec1, Vector vec2) { return (vec2 - vec1).Length(); };
bool EntIsInFieldOfView(CBaseEntity *pPlayer, CBaseEntity *pEnt);
bool FInViewCone(Vector *pOrigin, edict_t *pEdict);
void UTIL_HostSay( CBasePlayer *pPlayer, int teamonly, char *message );
bool UTIL_DBsAreGathered(void);
bool UTIL_DBsAreActive(void);
// Function code by cheesy
void UTIL_DamagePlayer(CBasePlayer *pKiller, CBasePlayer *pVictim, const char *Weapon, float Damage);
CBaseEntity *UTIL_CreateEnt( char *szName, const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner );
void UTIL_SpawnMonster(const char *szName, Vector Origin, Vector Angles, CBaseEntity *pOwner);
CBaseEntity *UTIL_GetLastAttackFromPlayer(CBasePlayer *pPlayer);
CBaseEntity *UTIL_GetAttackFromPlayerByName(CBasePlayer *pPlayer, const char *szName);
Vector GetNearestCollision(CBaseEntity *pEntity, float Range);
Vector GetFurthestCollision(CBaseEntity *pEntity, float Range);
float GetCollisionDist(CBaseEntity *pEnt, int iDir, float flTraceDist);
bool IsVisible(Vector v_vec1, Vector v_vec2, CBaseEntity *pEntIgnore);
inline bool UTIL_EntDoesntMove(CBaseEntity *pEntity) { return ((pEntity->pev->velocity.x == 0.0f) && (pEntity->pev->velocity.y == 0.0f) && (pEntity->pev->velocity.z == 0.0f)); };
bool UTIL_IsInBModel(Vector v_origin, CBaseEntity *pEnt);
char *UTIL_GetAnimName(CBaseEntity *pEnt, int Anim);

class CBot;
inline CBot *UTIL_GetBotPointer(CBasePlayer *pClient)	{ return (CBot *)pClient; };

// Debug functions
#ifdef _DEBUG
void DisplayDebugTexts(void);
void CreateTaskForEveryBot(ETaskCode TaskCode);
void CreateComboForEveryBot(EComboType CT);
Vector DebugVecCheckThrow ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flSpeed, float flGravityAdj );
#endif

// ==================================================================
// Code of TLinkedList - Start
// ==================================================================

template <class C>struct node_s
{
	C Entry;
	node_s *next;
	node_s *prev;

	node_s(void) : next(NULL), prev(NULL)
	{
		//memset(&Entry, 0, sizeof(Entry));
	};
};

template <class C> class TLinkedList
{
public:
	/* removed by harSens
	struct node_s
	{
		C Entry;
		node_s *next;
		node_s *prev;

		node_s(void) : next(NULL), prev(NULL)
		{
			//memset(&Entry, 0, sizeof(Entry));
		};
	};
	*/

	// member functions

	void AddNode(C entry)
	{
		if (!pNodeList)
		{
			pNodeList = new node_s<C>();
			pNodeList->Entry = entry;
			pNodeList->next = NULL;
			pNodeList->prev = NULL;
			pLastNode = pNodeList;
		}
		else
		{
			pLastNode->next = new node_s<C>();
			pLastNode->next->prev = pLastNode;
			pLastNode = pLastNode->next;
			pLastNode->Entry = entry;
			pLastNode->next = NULL;
		}
	}

	void PushNode(C Entry)
	{
		if (!pNodeList)
		{
			pNodeList = new node_s<C>;
			pNodeList->Entry = Entry;
			pNodeList->next = NULL;
			pNodeList->prev = NULL;
			pLastNode = pNodeList;
		}
		else
		{
			node_s<C> *pNew = new node_s<C>;
			pNew->Entry = Entry;
			pNew->prev = NULL;
			pNew->next = pNodeList;
			pNodeList->prev = pNew;
			pNodeList = pNew;
		}
	}

	void DeleteEntry(C Entry)
	{
		node_s<C> *pNode = pNodeList;
		if (!pNode)
			return;

		if (pNode->Entry == Entry) // first node
		{
			if (pNodeList == pLastNode)
				pLastNode = NULL;

			pNodeList = pNodeList->next;
			if (pNodeList)
				pNodeList->prev = NULL;
			pNode->next = NULL;
			delete pNode;
			pNode = NULL;
			return;
		}

		if (Entry == pLastNode->Entry) // last node
		{
			pNode = pLastNode;
			pLastNode = pLastNode->prev;
			pLastNode->next = NULL;

			pNode->next = NULL;
			pNode->prev = NULL;
			delete pNode;
			pNode = NULL;
			return;
		}

		// node is somewhere in the middle
		pNode = SearchNode(Entry);

		if (!pNode)
			return;

		node_s<C> *pPrevNode = pNode->prev;

		if (!pPrevNode)
			return;

		// unlink pNode
		pNode->next->prev = pPrevNode;
		pPrevNode->next = pNode->next;

		pNode->next = NULL;
		pNode->prev = NULL;
		delete pNode;
		pNode = NULL;
	}

	void DeleteNode(node_s<C> *pNode)
	{
		if (!pNode)
			return;

		if (pNodeList == pNode) // first node
		{
			if (pNodeList == pLastNode)
				pLastNode = pNodeList->next;

			pNodeList = pNodeList->next;
			if (pNodeList)
				pNodeList->prev = NULL;
			pNode->next = NULL;
			delete pNode;
			pNode = NULL;
			return;
		}

		if (pNode == pLastNode) // last node
		{
			pNode = pLastNode;
			pLastNode = pLastNode->prev;
			pLastNode->next = NULL;

			pNode->next = NULL;
			pNode->prev = NULL;
			delete pNode;
			pNode = NULL;
			return;
		}

		// node is somewhere in the middle

		node_s<C> *pPrevNode = pNode->prev;

		if (!pPrevNode)
			return;

		// unlink pNode
		pNode->next->prev = pPrevNode;
		pPrevNode->next = pNode->next;

		pNode->next = NULL;
		pNode->prev = NULL;
		delete pNode;
		pNode = NULL;
	}

	void DeleteAllNodes(void)
	{
		node_s<C> *pNode = pNodeList;
		node_s<C> *pTemp;
		while (pNode != NULL)
		{
			pTemp = pNode;
			pNode = pNode->next;
			pTemp->next = NULL;
			pTemp->prev = NULL;
			delete pTemp;
		}
		pNodeList = pLastNode = NULL;
	}

	node_s<C> *SearchNode(C Entry)
	{
		node_s<C> *pNode = pNodeList;
		while(pNode)
		{
			if (pNode->Entry == Entry)
				return pNode;
			pNode = pNode->next;
		}
		return NULL;
	}

	C Pop(void)
	{
		if (!pNodeList)
			return static_cast<C>(NULL);

		C Entry = pNodeList->Entry;
		DeleteNode(pNodeList);
		return Entry;
	}

	node_s<C> *GetFirst(void) { return pNodeList; };
	node_s<C> *GetLast(void) { return pLastNode; };

	void EditEntry(C OrigEntry, C NewVal)
	{
		node_s<C> *pNode = SearchNode(OrigEntry);

		if (pNode)
			pNode->Entry = NewVal;
	}

	bool IsInList(C Entry)
	{
		return (SearchNode(Entry) != NULL);
	}

	bool Empty(void)		{ return (pNodeList == NULL); };

	// construction/destruction
	TLinkedList(void)
	{
		pNodeList = NULL; 
		pLastNode = NULL;
	};

	~TLinkedList(void)
	{
		DeleteAllNodes();
	};

private:
	node_s<C> *pNodeList;
	node_s<C> *pLastNode;
};

// ==================================================================
// Code of TLinkedList - End
// ==================================================================

// ==================================================================
// Code of TPriorList - Begin
// ==================================================================

template <class C, class D=int> class TPriorList
{
public:
	struct node_s
	{
		C Entry;
		D Priority;
		node_s *next;

		node_s(C Ent, D Prior) : Entry(Ent), Priority(Prior), next(NULL) {};
	};

	TPriorList(void) : pHeadNode(NULL), pLastNode(NULL) {};
	~TPriorList(void) { Clear(); };

	void AddEntry(C Entry, D Prior)
	{
		if (!pHeadNode)
		{
			pHeadNode = new node_s(Entry, Prior);
			pLastNode = pHeadNode;
			iNodeCount=0;
		}
		else
		{
			iNodeCount++;
			node_s *pNew = new node_s(Entry, Prior);
			node_s *pNode = pHeadNode;
			node_s *pPrev = NULL;

			while(pNode)
			{
				if (Prior < pNode->Priority)
				{
					if (!pPrev)
					{
						pNew->next = pNode;
						pHeadNode = pNew;
					}
					else
					{
						pPrev->next = pNew;
						pNew->next = pNode;
					}
					break;
				}
				pPrev = pNode;
				pNode = pNode->next;
			}

			if (!pNode)
			{
				pLastNode = pNew;
				if (pPrev)
					pPrev->next = pNew;
			}
		}
	}
	
	C Pop(void)
	{
		if (!pHeadNode)
			return static_cast<C>(NULL);

		C Entry = pHeadNode->Entry;
		DeleteNode(pHeadNode);
		return Entry;
	}

	void Clear(void)
	{
		node_s *pTemp;
		while(pHeadNode)
		{
			pTemp = pHeadNode;
			pHeadNode = pHeadNode->next;
			delete pTemp;
		}

		pHeadNode = pLastNode = NULL;
	}

	bool IsInList(C Entry, D Prior)
	{
		node_s *pNode = pHeadNode;
		while(pNode)
		{
			if (pNode->Entry == Entry)
				return true;

			if (Prior < pNode->Priority)
				return false;

			pNode = pNode->next;
		}

		return false;
	}

	bool Empty(void) { return (pHeadNode == NULL); }
	node_s *GetFirst(void) { return pHeadNode; }
private:
	node_s *pHeadNode;
	node_s *pLastNode;
	int iNodeCount;

	node_s *GetPrevNode(node_s *pNode)
	{
		node_s *pTemp = pHeadNode;
		while(pTemp)
		{
			if (pTemp->next == pNode)
				return pTemp;
			pTemp = pTemp->next;
		}

		return NULL;
	}

	void DeleteNode(node_s *pNode)
	{
		if (!pNode)
			return;

		if (pHeadNode == pNode) // first node
		{
			if (pHeadNode == pLastNode)
				pLastNode = pHeadNode->next;

			pHeadNode = pHeadNode->next;
			pNode->next = NULL;
			delete pNode;
			pNode = NULL;
			iNodeCount--;
			return;
		}

		if (pNode == pLastNode) // last node
		{
			pNode = pLastNode;
			pLastNode->next = NULL;

			pNode->next = NULL;
			delete pNode;
			pNode = NULL;
			iNodeCount--;
			return;
		}

		// node is somewhere in the middle

		node_s *pPrevNode = GetPrevNode(pNode);
		if (!pPrevNode)
			return;

		// unlink pNode
		pPrevNode->next = pNode->next;
		pNode->next = NULL;
		delete pNode;
		pNode = NULL;
		iNodeCount--;
	}
};

// ==================================================================
// Code of TPriorList - End
// ==================================================================


// ==================================================================
// Code of TMutiChoice - Begin
// ==================================================================

template <class C> class TMultiChoice 
{
	struct SMultiChoice
	{
		int MinVal;
		int MaxVal;
		C Choice;
		SMultiChoice(void) : MinVal(0), MaxVal(0){};
	};

	int TotalVal;
	TLinkedList<SMultiChoice*> *pChoiceList;

public:
	TMultiChoice(void) : pChoiceList(new TLinkedList<SMultiChoice*>), TotalVal(0) // Constructor
	{
	};
	
	~TMultiChoice(void) // Destructor
	{
		while(pChoiceList->Empty() == false)
		{
			SMultiChoice *pEntry = pChoiceList->Pop();
			if (pEntry)
				delete pEntry;
			else
				break;
		}
		delete pChoiceList;
		pChoiceList = NULL;
	}

	void Insert(C Choice, short Percent = 50)
	{
		if (Percent == 0)
			return;

		SMultiChoice *pChoiceEntry = new SMultiChoice;
		
		pChoiceEntry->MinVal = TotalVal;
		pChoiceEntry->MaxVal = TotalVal + Percent;
		pChoiceEntry->Choice = Choice;

		pChoiceList->AddNode(pChoiceEntry);

		TotalVal += Percent;
	};

	bool FindSelection(SMultiChoice *MS, int Choice)
	{
		if ((Choice >= MS->MinVal) && (Choice < MS->MaxVal))
		{
			return true;
		}
		return false;
	}

	void ClearChoices(void)
	{
		TotalVal = 0;
		while(pChoiceList->Empty() == false)
			delete pChoiceList->Pop();
	}

	bool GetSelection(C &Var)
	{
		int Choice = RANDOM_LONG(0, (TotalVal - 1));
		/* modified by harSens
		TLinkedList<SMultiChoice*>::node_s *pNode;
		*/
		node_s<SMultiChoice*>*pNode;
		pNode = pChoiceList->GetFirst();

		while(pNode)
		{
			if ((Choice >= pNode->Entry->MinVal) && (Choice < pNode->Entry->MaxVal))
			{
				Var = pNode->Entry->Choice;
				return true;
			}
			pNode = pNode->next;
		}

		return false;
	}
};

// ==================================================================
// Code of TMutiChoice - End
// ==================================================================


// ==================================================================
// Structure for info about surrounding attacks
// ==================================================================

struct SSurroundingAttacksInfo
{
	edict_t *pMostDangerousAttack;
	byte DangerLevel;	// How much danger is there for all attacks?
	byte NumAttacks;	// How much visible attacks are hostile?
	SSurroundingAttacksInfo(void) : pMostDangerousAttack(NULL), DangerLevel(0), NumAttacks(0) {};
};


// ==================================================================
// Code of CAttackManager - Start
// ==================================================================

class CAttackManager
{
	struct attack_node_s
	{
		CBaseEntity *pAttack;
		float CreateTime;

		// for sbots
		bool Attacked;
		float AttackedTime;
		Vector OldVelocity;

		attack_node_s(CBaseEntity *pEntity, float CT) : pAttack(pEntity), CreateTime(CT),
														Attacked(false), AttackedTime(0.0),
														OldVelocity(g_vecZero) {};
		~attack_node_s(void) { pAttack = NULL; };

		attack_node_s &operator=(attack_node_s *p) { pAttack = p->pAttack; return *this; };
	};

	TLinkedList<attack_node_s*> AttackList;

public:

	CAttackManager(void); // Construction
	~CAttackManager(void); // Destruction

	void AddAttack(CBaseEntity *pAttack);
	void ClearList(void)	{ AttackList.DeleteAllNodes(); };
	void Think(void);

	bool IsHostileAttack(CBasePlayer *pPlayer, CBaseEntity *pAttack, bool CheckFOV);
	bool AttackIsInList(CBaseEntity *pAttack);

	edict_t *GetNearestHostileAttack(CBasePlayer *pPlayer, float Radius, bool CheckFOV);
	edict_t *GetNearestHostileAttack(CBasePlayer *pPlayer, float MinDist, float MaxDist,
									 bool CheckFOV);
	short CountHostileAttacks(CBasePlayer *pPlayer, float Radius, bool CheckFOV);
	short CountAttacksByName(CBasePlayer *pPlayer, const char *Name);
	// returns -1 if no attack found
	float GetNearestAttackDistByName(CBasePlayer *pPlayer, const char *Name);
	// returns -1 if no attack found
	float GetFurthestAttackDistByName(CBasePlayer *pPlayer, const char *Name);

	CBasePlayer *SearchLastAttack(edict_t *pPlayer);
	CBasePlayer *SearchAttackByName(edict_t *pPlayer, const char *szAttackName);

	// For debugging
	void PrintEntry(attack_node_s *pNode);
	void PrintAttackList(void);
};


// ==================================================================
// Code of CAttackManager - End
// ==================================================================

#endif