/*
* 怪物群体监控器 / Monster group monitor
* 作者(Author): Ice Box
* Steam: https://steamcommunity.com/id/ibox666/
* 如需使用此脚本请联系作者 / If you need to use this script, please contact the author
*/

namespace PointMonsterGroupWatcher
{

const int SF_START_OFF = 1 << 0;
const int SF_PASS_ACTIVATOR = 1 << 1;
const int SF_NO_NEW_MONSTERS = 1 << 2;

class CPointMonsterGroupWatcher : ScriptBaseEntity
{
	private bool m_bEnabled = true;
	private bool m_bWatching = false;
	private float m_flDelay = 0.0;
	private float m_flCheckInterval = 0.1;
	private string m_szMonsterTargetname = "";
	private string m_szKillTarget = "";
	private EHandle m_hActivator;
	private array<EHandle> m_hMonsters;
	private array<EHandle> m_hProcessedDeadMonsters;
	private CScheduledFunction@ m_pWatchThink;
	private CScheduledFunction@ m_pDelayedTrigger;

	bool KeyValue(const string& in szKey, const string& in szValue)
	{
		if(szKey == "monster_targetname")
		{
			m_szMonsterTargetname = szValue;
			return true;
		}
		else if(szKey == "delay")
		{
			m_flDelay = atof(szValue);
			if(m_flDelay < 0.0)
				m_flDelay = 0.0;
			return true;
		}
		else if(szKey == "check_interval")
		{
			m_flCheckInterval = atof(szValue);
			if(m_flCheckInterval <= 0.0)
				m_flCheckInterval = 0.1;
			return true;
		}
		else if(szKey == "killtarget")
		{
			m_szKillTarget = szValue;
			return true;
		}
		else
		{
			return BaseClass.KeyValue(szKey, szValue);
		}
	}

	void Spawn()
	{
		if((self.pev.spawnflags & SF_START_OFF) != 0)
		{
			m_bEnabled = false;
		}

		if(m_bEnabled)
		{
			StartWatching();
		}
	}

	void Use(CBaseEntity@ pActivator, CBaseEntity@ pCaller, USE_TYPE useType, float flValue)
	{
		m_hActivator = pActivator;

		switch(useType)
		{
			case USE_ON:
				if(!m_bEnabled)
				{
					m_bEnabled = true;
					StartWatching();
				}
				break;

			case USE_OFF:
				if(m_bEnabled)
				{
					m_bEnabled = false;
					StopWatching();
				}
				break;

			case USE_TOGGLE:
				if(m_bEnabled)
				{
					m_bEnabled = false;
					StopWatching();
				}
				else
				{
					m_bEnabled = true;
					StartWatching();
				}
				break;

			case USE_SET:
				if(m_bEnabled)
				{
					StopWatching();
					StartWatching();
				}
				else
				{
					m_bEnabled = true;
					StartWatching();
				}
				break;

			case USE_KILL:
				g_EntityFuncs.Remove(self);
				break;
		}
	}

	void StartWatching()
	{
		if(m_bWatching)
		{
			return;
		}

		if(m_szMonsterTargetname == "")
		{
			return;
		}

		m_bWatching = true;

		m_hMonsters.resize(0);
		m_hProcessedDeadMonsters.resize(0);

		UpdateMonsterList();

		@m_pWatchThink = g_Scheduler.SetInterval(this, "WatchThink", m_flCheckInterval);
	}

	void StopWatching()
	{
		if(!m_bWatching)
		{
			return;
		}

		m_bWatching = false;

		if(m_pWatchThink !is null)
		{
			g_Scheduler.RemoveTimer(m_pWatchThink);
			@m_pWatchThink = null;
		}
	}

	void UpdateMonsterList()
	{
		CBaseEntity@ pEntity = null;
		uint entityCount = 0;
		while((@pEntity = g_EntityFuncs.FindEntityByTargetname(pEntity, m_szMonsterTargetname)) !is null)
		{
			entityCount++;

			if(pEntity is null || pEntity.pev is null)
			{
				continue;
			}

			if(!pEntity.IsMonster())
			{
				continue;
			}

			bool bAlreadyTracked = false;
			for(uint i = 0; i < m_hMonsters.length(); i++)
			{
				CBaseEntity@ pTrackedMonster = m_hMonsters[i].GetEntity();
				if(pTrackedMonster !is null && pTrackedMonster is pEntity)
				{
					bAlreadyTracked = true;
					break;
				}
			}

			if(!bAlreadyTracked)
			{
				if(pEntity.pev.deadflag != DEAD_NO)
				{
					bool bAlreadyProcessed = false;
					for(uint j = 0; j < m_hProcessedDeadMonsters.length(); j++)
					{
						CBaseEntity@ pProcessedMonster = m_hProcessedDeadMonsters[j].GetEntity();
						if(pProcessedMonster !is null && pProcessedMonster is pEntity)
						{
							bAlreadyProcessed = true;
							break;
						}
					}

					if(!bAlreadyProcessed)
					{
						CBaseEntity@ pActivator = null;
						if((self.pev.spawnflags & SF_PASS_ACTIVATOR) != 0)
						{
							@pActivator = m_hActivator.GetEntity();
						}

						DelayedTrigger(@pActivator);

						m_hProcessedDeadMonsters.insertLast(EHandle(pEntity));
					}
				}
				else if(pEntity.IsAlive())
				{
					m_hMonsters.insertLast(EHandle(pEntity));
				}
			}
		}
	}

	void WatchThink()
	{
		if(!m_bWatching)
		{
			StopWatching();
			return;
		}

		for(uint i = 0; i < m_hMonsters.length(); i++)
		{
			CBaseEntity@ pMonster = m_hMonsters[i].GetEntity();

			bool bIsDead = false;
			if(pMonster is null || pMonster.pev is null)
			{
				bIsDead = true;
			}
			else if(pMonster.pev.deadflag != DEAD_NO)
			{
				bIsDead = true;
			}

			if(bIsDead)
			{
				CBaseEntity@ pActivator = null;
				if((self.pev.spawnflags & SF_PASS_ACTIVATOR) != 0)
				{
					@pActivator = m_hActivator.GetEntity();
				}

				if(m_flDelay > 0.0)
				{
					if(m_pDelayedTrigger !is null)
					{
						g_Scheduler.RemoveTimer(m_pDelayedTrigger);
						@m_pDelayedTrigger = null;
					}

					@m_pDelayedTrigger = g_Scheduler.SetTimeout(this, "DelayedTrigger", m_flDelay, @pActivator);
				}
				else
				{
					DelayedTrigger(@pActivator);
				}

				if(pMonster !is null && pMonster.pev !is null)
				{
					m_hProcessedDeadMonsters.insertLast(EHandle(pMonster));
				}

				m_hMonsters.removeAt(i);
				i--;
			}
		}

		if((self.pev.spawnflags & SF_NO_NEW_MONSTERS) == 0)
		{
			UpdateMonsterList();
		}
	}

	void DelayedTrigger(CBaseEntity@ pActivator)
	{
		string szMessage = string(self.pev.message);
		if(szMessage != "")
		{
			g_PlayerFuncs.ClientPrintAll(HUD_PRINTTALK, szMessage + "\n");
		}

		if(self.pev.target != "")
		{
			g_EntityFuncs.FireTargets(string(self.pev.target), pActivator, self, USE_TOGGLE, 0.0);
		}

		if(m_szKillTarget != "")
		{
			CBaseEntity@ pKillTarget = null;
			while((@pKillTarget = g_EntityFuncs.FindEntityByTargetname(pKillTarget, m_szKillTarget)) !is null)
			{
				g_EntityFuncs.Remove(pKillTarget);
			}
		}
	}

	void UpdateOnRemove()
	{
		if(m_pWatchThink !is null)
		{
			g_Scheduler.RemoveTimer(m_pWatchThink);
			@m_pWatchThink = null;
		}

		if(m_pDelayedTrigger !is null)
		{
			g_Scheduler.RemoveTimer(m_pDelayedTrigger);
			@m_pDelayedTrigger = null;
		}

		BaseClass.UpdateOnRemove();
	}
}

void Register()
{
	g_CustomEntityFuncs.RegisterCustomEntity("PointMonsterGroupWatcher::CPointMonsterGroupWatcher", "point_monster_group_watcher");
}

}