#include "BulletEjection"
#include "BuyMenu"
#include "weapon_ak47"
#include "weapon_m4a1"
#include "weapon_aug"
#include "weapon_sg552"
#include "weapon_famas"
#include "weapon_galil"
#include "weapon_csm249"
#include "weapon_awp"
#include "weapon_sg550"
#include "weapon_g3sg1"
#include "weapon_scout"
#include "weapon_csknife"
#include "weapon_hegrenade"
#include "weapon_xm1014"
#include "weapon_m3"
#include "weapon_ump45"
#include "weapon_tmp"
#include "weapon_mac10"
#include "weapon_p90"
#include "weapon_mp5navy"
#include "weapon_csdeagle"
#include "weapon_csglock18"
#include "weapon_fiveseven"
#include "weapon_dualelites"
#include "weapon_p228"
#include "weapon_usp"
#include "weapon_c4"
#include "weapon_ofshockrifle2"
#include "weapon_hlmp5"
#include "weapon_hlglock"
#include "weapon_hlshotgun"
#include "weapon_cofbooklaser"
#include "weapon_cofsyringe"
#include "weapon_qnailgun"
#include "weapon_beartrap"
//#include "weapon_scorpion"
#include "weapon_clhornetgun"
//#include "weapon_barniel"
#include "weapon_bow"
#include "cs16ammo"
#include "items2"
#include "projectile"
#include "tempents"
#include "triggers"

const bool g_bCrackLifeMode = false;
const bool g_WeaponMode = true;

BuyMenu::BuyMenu g_BuyMenu;

void RegisterCS16()
{
	g_BuyMenu.RemoveItems();
//	RegisterHEGRENADE();
	RegisterCSDeagle();
	RegisterFIVESEVEN();
	RegisterGLOCK18();
	RegisterUSP();
	RegisterP228();
	RegisterELITES();
	RegisterMP5Navy();
	RegisterP90();
	RegisterUMP45();
	RegisterTMP();
	RegisterMAC10();
	RegisterAK47();
	RegisterM4A1();
	RegisterAUG();
	RegisterM3Shotty();
	RegisterXM1014Shotty();
	RegisterAWP();
	RegisterSG552();
	RegisterCSM249();
	RegisterFAMAS();
	RegisterGALIL();
	RegisterSG550();
	RegisterG3SG1();
	RegisterCSKNIFE();
	RegisterSCOUT();
	RegisterC4();
	RegisterCSAmmo();
	opfor_shockrifle::Register();
	RegisterHLMP5();
	RegisterHLGLOCK();
	RegisterHLShotgun();
	RegisterCoFBOOKLASER();
	RegisterCoFSYRINGE();
	q1_RegisterProjectiles();
	q1_RegisterWeapon_NAILGUN();
	HLWanted_Beartrap::Register();
//	HLWanted_Scorpion::Register();
	HLWanted_Bow::Register();
	HIVEHAND::Register();
	CS16_HEGRENADE::Register();
//	HLweapon_Barniel::Register();
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<7> Ammo 556 Nato", "ammo_cs_556", 0 ,false ) );	// false = personally , true = globally
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<8> Ammo 762 Nato", "ammo_cs_762", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<4> Ammo 9mm Para", "ammo_cs_9mm", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<5> Ammo FN 57", "ammo_cs_fn57", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<7> Ammo 50 Ae", "ammo_cs_50ae", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<3> Ammo 45 Acp", "ammo_cs_45acp", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<2> Ammo 357 Sig", "ammo_cs_357sig", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<6> Ammo 12 Gauge", "ammo_cs_buckshot", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<15> Ammo 338 Lapua", "ammo_cs_338lapua", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<12> Ammo 556 Nato Heavy", "ammo_cs_556box", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<12> HL-MP5 Grenades", "ammo_ARgrenades", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<12> Spore (Sporelauncher)", "ammo_sporeclip", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<12> HL-Wanted Bow Arrows", "ammo_bow", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<5> CS Knife", "weapon_csknife", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<9> USP", "weapon_usp", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<8> Glock 18", "weapon_csglock18", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<6> P-228", "weapon_p228", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<8> Five Seven", "weapon_fiveseven", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<14> Dual Elites", "weapon_dualelites", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<18> Desert Eagle", "weapon_csdeagle", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<20> HE Grenade", "weapon_hegrenade", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<35> C4", "weapon_c4", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<55> M3 Super", "weapon_m3", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<50> XM-1014", "weapon_xm1014", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<25> TMP", "weapon_tmp", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<21> Mac-10", "weapon_mac10", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<30> MP5 Navy", "weapon_mp5navy", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<24> Ump-45", "weapon_ump45", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<45> P-90", "weapon_p90", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<50> Galil", "weapon_galil", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<55> Famas", "weapon_famas", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<60> M4a1", "weapon_m4a1", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<65> AK-47", "weapon_ak47", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<70> AUG", "weapon_aug", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<70> SG-552", "weapon_sg552", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<55> Scout", "weapon_scout", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<95> AWP", "weapon_awp", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<85> G3-SG1", "weapon_g3sg1", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<78> SG-550", "weapon_sg550", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> M249", "weapon_csm249", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> ShockRifle", "weapon_ofshockrifle", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> Medkit", "weapon_medkit", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> Handgrenade", "weapon_handgrenade", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> Tripmine", "weapon_tripmine", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> Satchel Charge", "weapon_satchel", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> HL-MP5", "weapon_hlmp5", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> HL-GLOCK", "weapon_hlglock", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> HL-SHOTGUN", "weapon_hlshotgun", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> Book-Laser", "weapon_cofbooklaser", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> Syringe", "weapon_cofsyringe", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> Sporelauncher", "weapon_sporelauncher", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> Shockroach", "monster_shockroach", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> Quake-1-Nailgun", "weapon_qnailgun", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> HL-Wanted Bear Trap", "weapon_beartrap", 0 ,false ) );
//	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> HL-Wanted Scorpion", "weapon_scorpion", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> HL-Wanted Bow", "weapon_bow", 0 ,false ) );
	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> Hivehand", "weapon_hivehand", 0 ,false ) );
//	g_BuyMenu.AddItem( BuyMenu::BuyableItem( "<75> Barniel Spawner", "weapon_barniel", 0 ,false ) );
	g_Hooks.RegisterHook( Hooks::Player::ClientSay, @ClientSay );
}

class CBaseCustomWeapon : ScriptBasePlayerWeaponEntity
{
	// Possible workaround for the SendWeaponAnim() access violation crash.
	// According to R4to0 this seems to provide at least some improvement.
	// GeckoN: TODO: Remove this once the core issue is addressed.
	protected CBasePlayer@ m_pPlayer
	{
		get const { return cast<CBasePlayer@>( self.m_hPlayer.GetEntity() ); }
		set { self.m_hPlayer = EHandle( @value ); }
	}

	protected bool m_fDropped;
	CBasePlayerItem@ DropItem()
	{
		m_fDropped = true;
		return self;
	}

	void GetDefaultShellInfo( CBasePlayer@ pPlayer, Vector& out ShellVelocity,
		Vector& out ShellOrigin, float forwardScale, float rightScale, float upScale )
	{
		Vector vecForward, vecRight, vecUp;

		g_EngineFuncs.AngleVectors( pPlayer.pev.v_angle, vecForward, vecRight, vecUp );

		const float fR = Math.RandomFloat( 50, 70 );
		const float fU = Math.RandomFloat( 100, 150 );

		for( int i = 0; i < 3; ++i )
		{
			ShellVelocity[i] = pPlayer.pev.velocity[i] + vecRight[i] * fR + vecUp[i] * fU + vecForward[i] * 25;
			ShellOrigin[i]   = pPlayer.pev.origin[i] + pPlayer.pev.view_ofs[i] + vecUp[i] * upScale + vecForward[i] * forwardScale + vecRight[i] * rightScale;
		}
	}
}

class CBaseCustomAmmo : ScriptBasePlayerAmmoEntity
{
	protected string m_strModel = "models/error.mdl";
	protected string m_strName;
	protected int m_iAmount = 0;
	protected int m_iMax = 0;

	protected string m_strPickupSound = "items/gunpickup2.wav";

	void Precache()
	{
		g_Game.PrecacheModel( m_strModel );

		g_SoundSystem.PrecacheSound( m_strPickupSound );
	}

	void Spawn()
	{
		Precache();
		g_EntityFuncs.SetModel( self, m_strModel );
		BaseClass.Spawn();
	}

	bool AddAmmo( CBaseEntity@ pOther )
	{
		if ( pOther.GiveAmmo( m_iAmount, m_strName, m_iMax, false ) == -1 )
			return false;

		g_SoundSystem.EmitSound( self.edict(), CHAN_ITEM, m_strPickupSound, 1, ATTN_NORM );

		return true;
	}
}

class CBaseCustomMonster : ScriptBaseMonsterEntity
{
	int SF_MONSTER_GAG = 2;
	int SF_MONSTER_FADECORPSE = 512;

	Vector VecCheckToss( edict_t@& in pEdict, const Vector& in vecSpot1, Vector vecSpot2, const float flGravityAdj = 1.0 )
	{
		TraceResult	tr;
		Vector		vecMidPoint;// halfway point between Spot1 and Spot2
		Vector		vecApex;// highest point 
		Vector		vecScale;
		Vector		vecGrenadeVel;
		Vector		vecTemp;
		//float flGravity = g_EngineFuncs.CVarGetFloat( "sv_gravity" ) * flGravityAdj;
		float flGravity = 800.0f * flGravityAdj;

		if( vecSpot2.z - vecSpot1.z > 500 )
			return g_vecZero; // to high, fail

		g_EngineFuncs.MakeVectors(pev.angles);

		// toss a little bit to the left or right, not right down on the enemy's bean (head). 
		vecSpot2 = vecSpot2 + g_Engine.v_right * ( Math.RandomFloat(-8,8) + Math.RandomFloat(-16,16) );
		vecSpot2 = vecSpot2 + g_Engine.v_forward * ( Math.RandomFloat(-8,8) + Math.RandomFloat(-16,16) );

		// calculate the midpoint and apex of the 'triangle'
		// UNDONE: normalize any Z position differences between spot1 and spot2 so that triangle is always RIGHT

		// How much time does it take to get there?

		// get a rough idea of how high it can be thrown
		vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5;
		g_Utility.TraceLine(vecMidPoint, vecMidPoint + Vector(0,0,500), ignore_monsters, self.edict(), tr);
		vecMidPoint = tr.vecEndPos;
		// (subtract 15 so the grenade doesn't hit the ceiling)
		vecMidPoint.z -= 15;

		if( vecMidPoint.z < vecSpot1.z || vecMidPoint.z < vecSpot2.z )
			return g_vecZero; // to not enough space, fail

		// How high should the grenade travel to reach the apex
		float distance1 = (vecMidPoint.z - vecSpot1.z);
		float distance2 = (vecMidPoint.z - vecSpot2.z);

		// How long will it take for the grenade to travel this distance
		float time1 = sqrt( distance1 / (0.5 * flGravity) );
		float time2 = sqrt( distance2 / (0.5 * flGravity) );

		if( time1 < 0.1 )
			return g_vecZero; // too close

		// how hard to throw sideways to get there in time.
		vecGrenadeVel = (vecSpot2 - vecSpot1) / (time1 + time2);
		// how hard upwards to reach the apex at the right time.
		vecGrenadeVel.z = flGravity * time1;

		// find the apex
		vecApex  = vecSpot1 + vecGrenadeVel * time1;
		vecApex.z = vecMidPoint.z;

		g_Utility.TraceLine(vecSpot1, vecApex, dont_ignore_monsters, self.edict(), tr);
		if (tr.flFraction != 1.0)
			return g_vecZero; // fail!

		// UNDONE: either ignore monsters or change it to not care if we hit our enemy
		g_Utility.TraceLine(vecSpot2, vecApex, ignore_monsters, self.edict(), tr); 
		if (tr.flFraction != 1.0)
			return g_vecZero; // fail!

		return vecGrenadeVel;
	}

	Vector VecCheckThrow( edict_t@& in pEdict, const Vector& in vecSpot1, Vector vecSpot2, const float flSpeed, const float flGravityAdj = 1.0 )
	{
		//float flGravity = g_EngineFuncs.CVarGetFloat( "sv_gravity" ) * flGravityAdj;
		float flGravity = 800.0f * flGravityAdj;

		Vector vecGrenadeVel = (vecSpot2 - vecSpot1);

		// throw at a constant time
		float time = vecGrenadeVel.Length( ) / flSpeed;
		vecGrenadeVel = vecGrenadeVel * (1.0 / time);

		// adjust upward toss to compensate for gravity loss
		vecGrenadeVel.z += flGravity * time * 0.5;

		Vector vecApex = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5;
		vecApex.z += 0.5 * flGravity * (time * 0.5) * (time * 0.5);

		TraceResult tr;
		g_Utility.TraceLine(vecSpot1, vecApex, dont_ignore_monsters, pEdict, tr);
		if (tr.flFraction != 1.0)
				return g_vecZero; // fail!

		g_Utility.TraceLine(vecSpot2, vecApex, ignore_monsters, pEdict, tr);
		if (tr.flFraction != 1.0)
				return g_vecZero; // fail!

		return vecGrenadeVel;
	}
}

void WW2DynamicTracer( Vector start, Vector end, NetworkMessageDest msgType = MSG_PVS, edict_t@ dest = null )
{
	NetworkMessage WW2DT( msgType, NetworkMessages::SVC_TEMPENTITY, dest );
		WW2DT.WriteByte( TE_TRACER );
		WW2DT.WriteCoord( start.x );
		WW2DT.WriteCoord( start.y );
		WW2DT.WriteCoord( start.z );
		WW2DT.WriteCoord( end.x );
		WW2DT.WriteCoord( end.y );
		WW2DT.WriteCoord( end.z );
	WW2DT.End();
}

void WW2DynamicLight( Vector vecPos, int radius, int r, int g, int b, int8 life, int decay )
{
	NetworkMessage WW2DL( MSG_PVS, NetworkMessages::SVC_TEMPENTITY );
		WW2DL.WriteByte( TE_DLIGHT );
		WW2DL.WriteCoord( vecPos.x );
		WW2DL.WriteCoord( vecPos.y );
		WW2DL.WriteCoord( vecPos.z );
		WW2DL.WriteByte( radius );
		WW2DL.WriteByte( int(r) );
		WW2DL.WriteByte( int(g) );
		WW2DL.WriteByte( int(b) );
		WW2DL.WriteByte( life );
		WW2DL.WriteByte( decay );
	WW2DL.End();
}

HookReturnCode ClientSay( SayParameters@ pParams )
{
	CBasePlayer@ pPlayer = pParams.GetPlayer();
	const CCommand@ args = pParams.GetArguments();
	
	if( args.ArgC() == 1 && args.Arg(0) == "buy" || args.Arg(0) == "/buy" )
	{
        pParams.ShouldHide = true;
		g_BuyMenu.Show( pPlayer );
	}
	else if( args.ArgC() == 2 && args.Arg(0) == "buy" || args.Arg(0) == "/buy" )
	{
		pParams.ShouldHide = true;
		bool bItemFound = false;
		string szItemName;
		uint uiCost;

		if( g_BuyMenu.m_Items.length() > 0 )
		{
			for( uint i = 0; i < g_BuyMenu.m_Items.length(); i++ )
			{
				if( "weapon_" + args.Arg(1) == g_BuyMenu.m_Items[i].EntityName || "ammo_" + args.Arg(1) == g_BuyMenu.m_Items[i].EntityName )
				{
					bItemFound = true;
					szItemName = g_BuyMenu.m_Items[i].EntityName;
					uiCost = g_BuyMenu.m_Items[i].Cost;
					break;
				}
				else
					bItemFound = false;
			}

			if( bItemFound )
			{
				if(  pPlayer.pev.frags <= 1 )
				{
				g_PlayerFuncs.ClientPrint( pPlayer, HUD_PRINTTALK, "Not enough money(frags) - Cost: " + uiCost + "\n" );
				}
				else 
					if( uint(pPlayer.pev.frags) >= uiCost )
					{
						pPlayer.pev.frags -= uiCost;
						pPlayer.GiveNamedItem( szItemName );
					}
					else
					g_PlayerFuncs.ClientPrint( pPlayer, HUD_PRINTTALK, "Not enough money(frags) - Cost: " + uiCost + "\n" );
			}
			else
			{
				g_PlayerFuncs.ClientPrint( pPlayer, HUD_PRINTTALK, "Invalid item: " + args.Arg(1) + "\n" );
			}
		}
	}
	return HOOK_CONTINUE;
}