//=============================================================================
// Spooky event script
// Author: GeckoN
//=============================================================================

#include "point_spooky_gift"

int g_nGiftCnt;

const string GIFT_TARGET_PREFIX = "_Spooky_Gift_";
const int GIFT_TARGET_PREFIX_LEN = 11;

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void PluginInit()
{
	g_Module.ScriptInfo.SetAuthor( "Sven Co-op Development Team" );
	g_Module.ScriptInfo.SetContactInfo( "www.svencoop.com" );

	InitReplacements();

	g_Hooks.RegisterHook( Hooks::PickupObject::Materialize, @PickupObjectMaterialize );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
HookReturnCode PickupObjectMaterialize( CBaseEntity@ pEntity )
{
	if ( ( pEntity.pev.spawnflags & SF_CREATEDWEAPON ) != 0 )
		return HOOK_CONTINUE;

	string strTarget = pEntity.pev.target;
	if ( strTarget.IsEmpty() || strTarget.CompareN( GIFT_TARGET_PREFIX, GIFT_TARGET_PREFIX_LEN ) != 0 )
	{
		// Spawn gifts from mapper-placed boxes etc.?
		/*int nId = g_ReplacementMgr.FindMatch( pEntity.pev.classname, false );
		CModelReplacement @pRepl = g_ReplacementMgr.Get( nId );
		pRepl.Apply( pEntity );
		return HOOK_HANDLED;*/

		return HOOK_CONTINUE;
	}

	CBaseEntity@ pGift = g_EntityFuncs.FindEntityByTargetname( null, strTarget );
	if ( pGift is null )
		return HOOK_CONTINUE;

	pGift.pev.effects &= ~EF_NODRAW;
	g_EntityFuncs.SetModel( pEntity, "" );
	return HOOK_HANDLED;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void MapInit()
{
	g_nGiftCnt = 0;

	PointSpookyGift::Register();
	PointSpookyGift::PrecacheGlobal();
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void MapActivate()
{
	g_ReplacementMgr.CreateGifts();
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void InitReplacements()
{
	// Wildcard entries
	g_ReplacementMgr.Add( CModelReplacement( "ammo_*",					PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_SMALL ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_*",				PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_MEDIUM ) );

	// Entities that should retain their original model
	g_ReplacementMgr.Add( CModelReplacement( "ammo_spore",				"" ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_minigun",			"" ) );

	// Per-class entries
	g_ReplacementMgr.Add( CModelReplacement( "ammo_556",				PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_MEDIUM,		1.2 ) );
	g_ReplacementMgr.Add( CModelReplacement( "ammo_9mmbox",				PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_MEDIUM,		1.3 ) );
	g_ReplacementMgr.Add( CModelReplacement( "ammo_rpgclip",			PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_MEDIUM ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_medkit",			PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_MEDIUM,		1.3 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_egon",				PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_MEDIUM,		2.0 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_grapple",			PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_MEDIUM,		2.0 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_crossbow",			PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_MEDIUM,		2.0 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_displacer",		PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_MEDIUM,		2.0 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_snark",			PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_MEDIUM,		2.0 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_mp5",				PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE,		0.75,		90.0 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_9mmar",			PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE,		0.75,		90.0 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_9mmAR",			PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE,		0.75,		90.0 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_pipewrench",		PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE,		0.75 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_crowbar",			PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE,		0.75 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_shotgun",			PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_m16",				PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_m249",				PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE,		1.1 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_saw",				PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE,		1.1 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_sniperrifle",		PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_gauss",			PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_shockrifle",		PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_rpg",				PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE,		1.25 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_hornetgun",		PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE,		1.15 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weapon_sporelauncher",	PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_LARGE,		1.25 ) );
	g_ReplacementMgr.Add( CModelReplacement( "weaponbox",				PointSpookyGift::MODEL_GIFT,		PointSpookyGift::GIFT_MEDIUM,		1.5 ) );
}

//=============================================================================
// Model Replacement
//=============================================================================
class CModelReplacement
{
	string m_strClassname;
	string m_strModel;
	int m_nSubmodel;
	float m_fScale;
	float m_fAngle;

	CModelReplacement()
	{
	}

	CModelReplacement( const string& in strClassname, const string& in strModel = "", int nSubmodel = 0, float fScale = 1.0, float fAngle = 0.0 )
	{
		m_strClassname = strClassname;
		m_strModel = strModel;
		m_nSubmodel = nSubmodel;
		m_fScale = fScale;
		m_fAngle = fAngle;
	}

	bool IsWildcard()
	{
		int len = m_strClassname.Length();
		if ( len == 0 )
			return false;

		return m_strClassname[ len - 1 ] == '*';
	}

	bool Apply( CBaseEntity@ ent )
	{
		// Exception, do not replace!
		if ( m_strModel.IsEmpty() )
			return false;

		string strName = GIFT_TARGET_PREFIX + g_nGiftCnt;

		// Create, setup and spawn gift

		CBaseEntity@ pEntity = g_EntityFuncs.CreateEntity( PointSpookyGift::ENTITY_NAME, null, false );
		if ( pEntity is null )
			return false;

		// Some basic properties
		pEntity.pev.targetname = strName;
		pEntity.pev.maxs = ent.pev.maxs;
		pEntity.pev.mins = ent.pev.mins;
		pEntity.pev.origin = ent.pev.origin;
		pEntity.pev.angles = ent.pev.angles + Vector( 0, m_fAngle, 0 );
		pEntity.pev.movetype = ent.pev.movetype;
		pEntity.pev.solid = ent.pev.solid;
		// Pass on the target
		pEntity.pev.target = ent.pev.target;
		// Scale, relative to the original entity
		pEntity.pev.scale = ent.pev.scale != 0 ? ent.pev.scale * m_fScale : m_fScale;
		// Submodel (this is bit dirty but there is only one bodygroup so eh...)
		pEntity.pev.body = m_nSubmodel;
		// Spawn!
		g_EntityFuncs.DispatchSpawn( pEntity.edict() );

		// Hide the original entity and link it to the gift

		ent.pev.target = strName;
		g_EntityFuncs.SetModel( ent, "" );

		g_nGiftCnt++;
		return true;
	}
};

//=============================================================================
// Model Replacement Manager
//=============================================================================
class CModelReplacementManager
{
	array<CModelReplacement@> g_replacements;

	void Add( CModelReplacement @item )
	{
		g_replacements.insertLast( item );
	}

	CModelReplacement @ Get( int nId )
	{
		if ( nId < 0 || nId >= int( g_replacements.length() ) )
			return null;

		return g_replacements[ nId ];
	}

	int FindMatch( const string& in strClassname, bool bExactMatch )
	{
		for ( uint32 i = 0; i < g_replacements.length(); i++ )
		{
			CModelReplacement @item = g_replacements[ i ];
			if ( !bExactMatch && item.IsWildcard() )
			{
				int nLen = item.m_strClassname.Length() - 1;
				if ( item.m_strClassname.CompareN( strClassname, nLen ) == 0 && FindMatch( strClassname, true ) < 0 )
					return int( i );
			}
			else
			{
				if ( strClassname == item.m_strClassname )
					return int( i );
			}
		}

		return -1;
	}

	void CreateGifts()
	{
		for ( uint32 i = 0; i < g_replacements.length(); i++ )
		{
			CreateGift( g_replacements[ i ] );
		}
	}

	void CreateGift( CModelReplacement @item )
	{
		CBaseEntity@ ent = null;
		int nCount = 0;

		bool bWild = item.IsWildcard();

		while( ( @ent = g_EntityFuncs.FindEntityByClassname( ent, item.m_strClassname ) ) !is null )
		{
			// Wildcard classname? Skip exact matches!
			if ( bWild && FindMatch( ent.pev.classname, true ) >= 0 )
				continue;

			if ( item.Apply( ent ) )
				nCount++;
		}

		//g_Game.AlertMessage( at_console, "Classname: \"%1\", Model \"%2\", Replaced: %3\n", strClassname, strModel, nCount );
	}
};

CModelReplacementManager g_ReplacementMgr;
