//========= Copyright Valve Corporation, All rights reserved. ============//
//
//
//=====================================================================================//

#include "cbase.h"					// for pch
#include "props.h"
#include "filters.h"
#include "achievementmgr.h"

extern CAchievementMgr g_AchievementMgrPortal;

#define RADIO_MODEL_NAME "models/props/radio_reference.mdl"
//#define RADIO_DEBUG_SERVER

class CDinosaurSignal : public CBaseEntity 
{
public:
	DECLARE_DATADESC();
	DECLARE_SERVERCLASS();
	DECLARE_CLASS( CDinosaurSignal, CBaseEntity );
	void Spawn();
	int UpdateTransmitState();
#if RADIO_DEBUG_SERVER
	int DrawDebugTextOverlays( void );
#endif

	CNetworkString( m_szSoundName, 128 );
	CNetworkVar( float, m_flInnerRadius );
	CNetworkVar( float, m_flOuterRadius );
	CNetworkVar( int, m_nSignalID );
};

LINK_ENTITY_TO_CLASS( updateitem1, CDinosaurSignal );

BEGIN_DATADESC( CDinosaurSignal )
	DEFINE_AUTO_ARRAY( m_szSoundName, FIELD_CHARACTER ),
	DEFINE_FIELD( m_flOuterRadius, FIELD_FLOAT ),
	DEFINE_FIELD( m_flInnerRadius, FIELD_FLOAT ),
	DEFINE_FIELD( m_nSignalID, FIELD_INTEGER ),
END_DATADESC()

IMPLEMENT_SERVERCLASS_ST( CDinosaurSignal, DT_DinosaurSignal )
	SendPropString( SENDINFO(m_szSoundName) ), 
	SendPropFloat( SENDINFO(m_flOuterRadius) ),
	SendPropFloat( SENDINFO(m_flInnerRadius) ),
	SendPropInt( SENDINFO(m_nSignalID) ),
END_SEND_TABLE()

void CDinosaurSignal::Spawn()
{
	PrecacheScriptSound( m_szSoundName.Get() );
	BaseClass::Spawn();
	SetTransmitState( FL_EDICT_ALWAYS );
}

int CDinosaurSignal::UpdateTransmitState()
{
	// ALWAYS transmit to all clients.
	return SetTransmitState( FL_EDICT_ALWAYS );
}

#if RADIO_DEBUG_SERVER
int CDinosaurSignal::DrawDebugTextOverlays( void )
{
	int text_offset = BaseClass::DrawDebugTextOverlays();
	if (m_debugOverlays & OVERLAY_TEXT_BIT) 
	{
		NDebugOverlay::Sphere( GetAbsOrigin(), GetAbsAngles(), m_flInnerRadius, 255, 0, 0, 64, false, 0.1f );
		NDebugOverlay::Sphere( GetAbsOrigin(), GetAbsAngles(), m_flOuterRadius, 0, 255, 0, 64, false, 0.1f );
	}
	return text_offset;
}
#endif


class CPortal_Dinosaur : public CPhysicsProp 
{
public:
	DECLARE_CLASS( CPortal_Dinosaur, CPhysicsProp );
	DECLARE_DATADESC();
	DECLARE_SERVERCLASS();

	virtual void Spawn();
	virtual void Precache();
	virtual QAngle PreferredCarryAngles( void ) { return QAngle( 0, 180, 0 ); }
	virtual bool HasPreferredCarryAnglesForPlayer( CBasePlayer *pPlayer ) { return true; }
	virtual void Activate();


	CNetworkHandle( CDinosaurSignal, m_hDinosaur_Signal );
	CNetworkVar( bool, m_bAlreadyDiscovered );
};

LINK_ENTITY_TO_CLASS( updateitem2, CPortal_Dinosaur );

BEGIN_DATADESC( CPortal_Dinosaur )
	DEFINE_FIELD( m_hDinosaur_Signal, FIELD_EHANDLE ),
	DEFINE_FIELD( m_bAlreadyDiscovered, FIELD_BOOLEAN ),
END_DATADESC()

IMPLEMENT_SERVERCLASS_ST( CPortal_Dinosaur, DT_PropDinosaur )
	SendPropEHandle( SENDINFO( m_hDinosaur_Signal ) ),
	SendPropBool( SENDINFO( m_bAlreadyDiscovered ) ),
END_SEND_TABLE()

void CPortal_Dinosaur::Precache()
{
	PrecacheModel( RADIO_MODEL_NAME );

	PrecacheScriptSound( "Portal.room1_radio" );
	PrecacheScriptSound( "UpdateItem.Static" );
	PrecacheScriptSound( "UpdateItem.Dinosaur01" );
	PrecacheScriptSound( "UpdateItem.Dinosaur02" );
	PrecacheScriptSound( "UpdateItem.Dinosaur03" );
	PrecacheScriptSound( "UpdateItem.Dinosaur04" );
	PrecacheScriptSound( "UpdateItem.Dinosaur05" );
	PrecacheScriptSound( "UpdateItem.Dinosaur06" );
	PrecacheScriptSound( "UpdateItem.Dinosaur07" );
	PrecacheScriptSound( "UpdateItem.Dinosaur08" );
	PrecacheScriptSound( "UpdateItem.Dinosaur09" );
	PrecacheScriptSound( "UpdateItem.Dinosaur10" );
	PrecacheScriptSound( "UpdateItem.Dinosaur11" );
	PrecacheScriptSound( "UpdateItem.Dinosaur12" );
	PrecacheScriptSound( "UpdateItem.Dinosaur13" );
	PrecacheScriptSound( "UpdateItem.Dinosaur14" );
	PrecacheScriptSound( "UpdateItem.Dinosaur15" );
	PrecacheScriptSound( "UpdateItem.Dinosaur16" );
	PrecacheScriptSound( "UpdateItem.Dinosaur17" );
	PrecacheScriptSound( "UpdateItem.Dinosaur18" );
	PrecacheScriptSound( "UpdateItem.Dinosaur19" );
	PrecacheScriptSound( "UpdateItem.Dinosaur20" );
	PrecacheScriptSound( "UpdateItem.Dinosaur21" );
	PrecacheScriptSound( "UpdateItem.Dinosaur22" );
	PrecacheScriptSound( "UpdateItem.Dinosaur23" );
	PrecacheScriptSound( "UpdateItem.Dinosaur24" );
	PrecacheScriptSound( "UpdateItem.Dinosaur25" );
	PrecacheScriptSound( "UpdateItem.Dinosaur26" );

	PrecacheScriptSound( "UpdateItem.Fizzle" );
}


void CPortal_Dinosaur::Spawn()
{
	Precache();
	KeyValue( "model", RADIO_MODEL_NAME );
	m_spawnflags |= SF_PHYSPROP_START_ASLEEP;
	BaseClass::Spawn();
}

void CPortal_Dinosaur::Activate( void )
{
	// Find the current completion status of the dinosaurs
	uint64 fStateFlags = 0;
	CBaseAchievement *pTransmissionRecvd = dynamic_cast<CBaseAchievement *>(g_AchievementMgrPortal.GetAchievementByName("PORTAL_TRANSMISSION_RECEIVED"));
	if ( pTransmissionRecvd )
	{
		fStateFlags = pTransmissionRecvd->GetComponentBits();
	}

	if ( m_hDinosaur_Signal != NULL )
	{
		uint64 nId = m_hDinosaur_Signal.Get()->m_nSignalID;
		// See if we're already tripped
		if ( fStateFlags & ((uint64)1<<nId) )
		{
			m_bAlreadyDiscovered = true;
		}
	}

	BaseClass::Activate();
}

struct radiolocs 
{
	const char *mapname;
	const char *soundname;
	int			id;
	float		radiopos[3];
	float		radioang[3];
	float		soundpos[3];
	float		soundouterrad;
	float		soundinnerrad;
};
static const radiolocs s_radiolocs[] =
{
	{
		"testchmb_a_00",
		"UpdateItem.Dinosaur01",
		0,
		{ 0, 0, 0 },
		{ 0, 0, 0 },
		{ -506, -924, 161 },
		200,
		64
	},
	{
		"testchmb_a_00",
		"UpdateItem.Dinosaur02",
		1,
		{ -960, -634, 783 },
		{ 0, 90, 0 },
		{ -926.435, -256.323, 583 },
		200,
		64,
	},
	{
		"testchmb_a_01",
		"UpdateItem.Dinosaur03",
		2,
		{ 233, 393, 130 },
		{ 0, 225, 0 },
		{ 96, 160, -108 },
		224,
		128,
	},
	{
		"testchmb_a_01",
		"UpdateItem.Dinosaur04",
		3,
		{ -1439.89, 1076.04, 779.102 },
		{ 0, 270, 0 },
		{ -731, 735, 888 },
		400,
		64,
	},
	{
		// new entry
		"testchmb_a_02",
		"UpdateItem.Dinosaur05",
		4,
		{ 2, 65, 390 },
		{ 0, 270, 0 },
		{ -864, 192, 64},
		192,
		96,
	},
	{
		"testchmb_a_02",
		"UpdateItem.Dinosaur06",
		21,
		{ 111, 832, 577 },
		{ 0, 0, 0 },
		{ 918, 831, 512},
		192,
		96,
	},
	{
		"testchmb_a_03",
		"UpdateItem.Dinosaur07",
		5, 
		{ -53.2337, 78.181, 236 },
		{ 0, 225, 0 },
		{ 304, 0, -96 },
		256,
		128
	},
	// new entry
	{
		"testchmb_a_03",
		"UpdateItem.Dinosaur08",
		6, 
		{ 428.112, 0.22326, 1201 },
		{ 0, 180, 0 },
		{ -581.096, 193.694, 1351 },
		165,
		128
	},
	{
		"testchmb_a_04",
		"UpdateItem.Dinosaur09",
		7,
		{ 118, -56.6, -38.8 },
		{ 0, 180, 0 },
		{ -640, 256, 8 },
		512,
		128
	},
	// new entry
	{
		"testchmb_a_05",
		"UpdateItem.Dinosaur10",
		8, 
		{ 64, 144, 160 },
		{ 0, 270, 0 },
		{ 64, 740, 7 },
		350,
		128
	},
	{
		"testchmb_a_06",
		"UpdateItem.Dinosaur11",
		9, 
		{ 529, 315, 320 },
		{ 0, 270, 0 },
		{ 608, 128, -184 },
		384,
		160
	},
	{
		"testchmb_a_07",
		"UpdateItem.Dinosaur12",
		10, 
		{ 192, -1546, 1425 },
		{ 0, 113, 0 },
		{ 272, -496, 1328 },
		432,
		88
	},
	// new entry
	{
		"testchmb_a_07",
		"UpdateItem.Dinosaur13",
		11, 
		{ -144, -768, 256 },
		{ 0, 90, 0 },
		{ -192, -384, 176 },
		256,
		128
	},
	{
		"testchmb_a_08",
		"UpdateItem.Dinosaur14",
		12, 
		{ 267, -378, 256 },
		{ 0, 90, 0 },
		{ -560, 96, 320 },
		288,
		128,
	},
	{
		"testchmb_a_09",
		"UpdateItem.Dinosaur15",
		13, 
		{ 634, 1308, 256 },
		{ 0, 180, 0 },
		{ 386.699, 1792.43, 7},
		548,
		64
	},
	{
		"testchmb_a_10",
		"UpdateItem.Dinosaur16",
		14, 
		{ -1420, -2752, 76 },
		{ 0, 0, 0  },
		{ -1968, -2880, -334 },
		448,
		196,
	},
	// new entry
	{
		"testchmb_a_10",
		"UpdateItem.Dinosaur17",
		15, 
		{ 112, 1392, -63 },
		{ 0, 260, 0  },
		{ -189, 1220, 65 },
		192,
		128,
	},
	{
		"testchmb_a_11",
		"UpdateItem.Dinosaur18",
		16,
		{0,0,0},
		{0,0,0},
		{-512,644,64},
		192,
		96,
	},
	{
		"testchmb_a_13",
		"UpdateItem.Dinosaur19",
		17,
		{955,931,-267},
		{-90,0,0},
		{1472,-191,-12},
		256,
		128,
	},
	{
		"testchmb_a_14",
		"UpdateItem.Dinosaur20",
		18,
		{0,0,0},
		{0,0,0},
		{144,192,1288},
		807,
		128,
	},
	{
		"testchmb_a_14",
		"UpdateItem.Dinosaur21",
		22,
		{1285, 1344, 1412},
		{0,0,0},
		{2712, 894, 1011},
		200,
		120,
	},
	{
		"testchmb_a_14",
		"UpdateItem.Dinosaur22",
		23,
		{-952, 336, -256},
		{0,0,0},
		{-1144, -249, 3336},
		400,
		128,
	},
	{
		"testchmb_a_15",
		"UpdateItem.Dinosaur23",
		19,
		{-1529,293,-283},
		{0,90,0},
		{761,443,810},
		256,
		128,
	},
	{
		"escape_00",
		"UpdateItem.Dinosaur24",
		24,
		{192, -1344, -832},
		{0, 135, 0},
		{891, 322, -184},
		285,
		150,
	},
	{
		"escape_01",
		"UpdateItem.Dinosaur25",
		20,
		{0,0,0},
		{0,0,0},
		{-624, 1440, -464},
		512,
		128,
	},
	{
		"escape_02",
		"UpdateItem.Dinosaur26",
		25,
		{5504, 131, -1422},
		{0, 90, 0},
		{4218, 674, 8},
		300,
		100,
	},
};

class CSpawnDinosaurHack : CAutoGameSystem
{
public:
	virtual void LevelInitPreEntity();
	virtual void LevelInitPostEntity();

	CPortal_Dinosaur *SpawnDinosaur( radiolocs& loc );
	CDinosaurSignal *SpawnSignal( radiolocs& loc );

	void ApplyMapSpecificHacks();
};

static CSpawnDinosaurHack g_SpawnRadioHack;

void CSpawnDinosaurHack::LevelInitPreEntity()
{
	UTIL_PrecacheOther( "updateitem2", RADIO_MODEL_NAME );

	ApplyMapSpecificHacks();
}

// Spawn all the Dinosaurs and sstv images
void CSpawnDinosaurHack::LevelInitPostEntity() 
{
	if ( gpGlobals->eLoadType == MapLoad_LoadGame )
	{
#if defined ( RADIO_DEBUG_SERVER )
		Msg( "Not spawning any Dinosaurs: Detected a map load\n" );
#endif

		return;
	}

	IAchievement *pHeartbreaker = g_AchievementMgrPortal.GetAchievementByName("PORTAL_BEAT_GAME");
	if ( pHeartbreaker == NULL || pHeartbreaker->IsAchieved() == false )
	{
#if defined ( RADIO_DEBUG_SERVER )
		Msg( "Not spawning any Dinosaurs: Player has not beat the game, or failed to get heartbreaker achievement from mgr\n" );
#endif

		return;
	}

	for ( int i = 0; i < ARRAYSIZE( s_radiolocs ); ++i )
	{
		radiolocs loc = s_radiolocs[i];
		if ( V_strcmp( STRING(gpGlobals->mapname), loc.mapname ) == 0 )
		{
#if defined ( RADIO_DEBUG_SERVER )
			Msg( "Found Dinosaur and signal info for %s, spawning.\n", loc.mapname );
			Msg( "Dinosaur pos: %f %f %f, ang: %f %f %f\n", loc.radiopos[0], loc.radiopos[1], loc.radiopos[2], loc.radioang[0], loc.radioang[1], loc.radioang[2] );
			Msg( "Signal pos: %f %f %f, inner rad: %f, outter rad: %f\n", loc.soundpos[0], loc.soundpos[1], loc.soundpos[2], loc.soundinnerrad, loc.soundouterrad );
#endif
			
			CPortal_Dinosaur *pDinosaur = SpawnDinosaur( loc );
			CDinosaurSignal *pSignal = SpawnSignal( loc );

			Assert ( pDinosaur && pSignal );
			if ( pDinosaur && pSignal )
			{
#if defined ( RADIO_DEBUG_SERVER )
				Msg( "SUCCESS: Spawned Dinosaur and signal and linked them.\n" );
#endif
				// OK, so these really could have been the same class... not worth changing it now though.
				pDinosaur->m_hDinosaur_Signal.Set( pSignal );
				pDinosaur->Activate();
			}
		}
	}		 
}

CPortal_Dinosaur *CSpawnDinosaurHack::SpawnDinosaur( radiolocs& loc )
{
	Vector vSpawnPos ( loc.radiopos[0], loc.radiopos[1], loc.radiopos[2] );
	QAngle vSpawnAng ( loc.radioang[0], loc.radioang[1], loc.radioang[2] );

	// origin and angles of zero means skip this Dinosaur creation and look for an existing radio
	if ( loc.radiopos[0] == 0 && 
		loc.radiopos[1] == 0 && 
		loc.radiopos[2] == 0 && 
		loc.radioang[0] == 0 && 
		loc.radioang[1] == 0 && 
		loc.radioang[2] == 0 )
	{

#if defined ( RADIO_DEBUG_SERVER )
		Msg( "Dinosaur found with zero angles and origin. Replacing existing radio.\n" );
#endif
		// Find existing Dinosaur, kill it and spawn at its position
		CPhysicsProp *pOldDinosaur	= (CPhysicsProp*)gEntList.FindEntityByClassname( NULL, "prop_physics" );
		while ( pOldDinosaur )
		{
			if ( V_strcmp( STRING( pOldDinosaur->GetModelName() ), RADIO_MODEL_NAME ) == 0 )
			{
				vSpawnPos = pOldDinosaur->GetAbsOrigin();
				vSpawnAng = pOldDinosaur->GetAbsAngles();

				UTIL_Remove( pOldDinosaur );

#if defined ( RADIO_DEBUG_SERVER )
				Msg( "Found Dinosaur exiting in level, replacing with %f, %f %f and %f %f %f.\n", XYZ(vSpawnPos), XYZ(vSpawnAng) );
#endif
				break;
			}

			pOldDinosaur = (CPhysicsProp*)gEntList.FindEntityByClassname( pOldDinosaur, "prop_physics" );
		}
	}

	Assert( vSpawnPos != vec3_origin );

	CPortal_Dinosaur *pDinosaur = (CPortal_Dinosaur*)CreateEntityByName( "updateitem2" );
	Assert ( pDinosaur );
	if ( pDinosaur )
	{
		pDinosaur->SetAbsOrigin( vSpawnPos );
		pDinosaur->SetAbsAngles( vSpawnAng );
		DispatchSpawn( pDinosaur );
	}

	return pDinosaur;
}

CDinosaurSignal *CSpawnDinosaurHack::SpawnSignal( radiolocs& loc )
{
	CDinosaurSignal *pSignal = (CDinosaurSignal*)CreateEntityByName( "updateitem1" );
	Assert ( pSignal );
	if ( pSignal )
	{
#if defined ( RADIO_DEBUG_SERVER )
		if ( loc.soundinnerrad > loc.soundouterrad )
		{
			Assert( 0 );
			Warning( "Dinosaur BUG: Inner radius is greater than outer radius. Will swap them.\n" );
			swap( loc.soundinnerrad, loc.soundouterrad );
		}
#endif
		pSignal->SetAbsOrigin( Vector( loc.soundpos[0], loc.soundpos[1], loc.soundpos[2] ) );
		pSignal->m_flInnerRadius	= loc.soundinnerrad;
		pSignal->m_flOuterRadius	= loc.soundouterrad;
		V_strncpy( pSignal->m_szSoundName.GetForModify(), loc.soundname, 128 );
		pSignal->m_nSignalID		= loc.id;
		DispatchSpawn( pSignal );
	}

	return pSignal;
}


void CSpawnDinosaurHack::ApplyMapSpecificHacks()
{
	if ( V_strcmp( STRING(gpGlobals->mapname), "testchmb_a_02" ) == 0 )
	{
		CBaseEntity *pFilter = CreateEntityByName( "filter_activator_name" );
		Assert( pFilter );
		if ( pFilter )
		{
			pFilter->KeyValue( "filtername", "box_2" );
			pFilter->KeyValue( "targetname", "filter_weight_box" );
			DispatchSpawn( pFilter );
		}
	}
}