#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "effects.h"
#include "aura.h"
#include "classes.h"
#include "magicattack.h"
#include "explodingattack.h"
#include "player.h"
#include "gamerules.h"
#include "magicattack.h"
#include "explodingattack.h"
#include "beamattack.h"
#include "guidedattack.h"
#include "controlledbeam.h"
#include "powerstruggle.h"
#include "particletypes.h"

#define PS_KIRATIO 0.25
#define PS_DAMAGERATIO 0.5
#define MAX_BEAM_AIM_DIST 200

extern int gmsgCharge;
extern int gmsgPowerStruggle;
extern int gmsgAddEffect;
extern int gmsgRemoveEffect;
LINK_ENTITY_TO_CLASS( weapon_controlled, CControlledBeam );

BOOL CControlledBeam::Deploy( void )
{	InitChargeSprite();
	m_fBeamJumping=false;
	m_fBeamJump=false;
	m_eAttackState = ATTACK_IDLE;
	m_pFollower = NULL;
	return DefaultDeploy( m_pAttackName );	
}

void CControlledBeam::Precache()
{	PRECACHE_SOUND("weapons/beamhead.wav");
	PRECACHE_SOUND("weapons/beamfire.wav");
}

void CControlledBeam::Holster(int skiplocal)
{	
	
	if (m_eAttackState == ATTACK_AIM)
	{	DetonateAttack();
	}	
	else if (m_eAttackState == ATTACK_CHARGE)
	{	m_pPlayer->m_flSlowDown = 1.0;
		m_pPlayer->m_fCharging = false;
		
		//turn of charging sprite
		DisableChargeSprite();

		//turn all sounds off
		SilenceWeaponSounds();
		SilenceVoice();		
		SilenceBodySounds();

		/* Modified by Rick
		//undo drawing charge bar
		MESSAGE_BEGIN( MSG_ONE, gmsgCharge, NULL, m_pPlayer->pev ); 
			WRITE_BYTE(0);	//amount charged in %
		MESSAGE_END();*/
		
	}

	MESSAGE_BEGIN( MSG_ONE, gmsgRemoveEffect, NULL, m_pPlayer->edict() );
		WRITE_BYTE(BEAMBOXCROSSHAIR);				
	MESSAGE_END();
	m_pPlayer->DrawChargeBar(0);		
	RemoveChargeSprite();	
}

void CControlledBeam::EnableBeamJump()
{	m_fBeamJump = true;
	m_pMagic->SetTouch(&CGuidedAttack::StopTouch);	
}

void CControlledBeam::DisableBeamJump()
{	m_fBeamJump = false;
	if (m_pMagic->m_fStopped)
	{	//StopBeamJump();
		m_pMagic->Explode();
	}
	else
	{	m_pMagic->SetTouch(&CGuidedAttack::ExplodeTouch);
		UTIL_MakeVectors(m_pMagic->pev->angles);
		m_pMagic->pev->velocity = gpGlobals->v_forward * m_pMagic->m_flSpeed;		
		m_fBeamJumping = false;
	}
}

void CControlledBeam::StartBeamJump()
{	m_iOldMoveType = m_pPlayer->pev->movetype;
	m_pMagic->SetThink(NULL);
	
	m_pPlayer->pev->movetype = MOVETYPE_RIDEBEAM;
	m_pPlayer->m_flSlowDown = 5.0;
	Vector vecDir = m_pPlayer->pev->origin-m_pMagic->pev->origin;
	m_pPlayer->pev->vuser4 = vecDir.Normalize()*m_pPlayer->GetMaxSpeed();//maybe use the beam speed/dmg later?
	m_fBeamJumping = true;

	// Added by Pcjoe, re-fix direction of model when we beam jump
	UTIL_Model_FixAngles(m_pPlayer->entindex(), UTIL_VecToAngles(-vecDir));
}

void CControlledBeam::StopBeamJump()
{	// Edited by Pcjoe, leave movetype alone if we're in melee
	if(!m_pPlayer->m_Melee.InAdvMeleeBattle())
	{	m_pPlayer->pev->movetype = m_iOldMoveType;				
	}
	m_fBeamJumping = false;	
}

// Added by Pcjoe: Check for walljump
BOOL CControlledBeam::CheckWallJump( void )
{	if(m_pPlayer->m_fWallJump)
	{	m_pPlayer->m_fCharging = false;
		m_eAttackState = ATTACK_IDLE;
		m_iCharge = 0;

		//turn of charging sprite
		DisableChargeSprite();

		//turn all sounds off
		SilenceWeaponSounds();
		SilenceVoice();		
		SilenceBodySounds();

		/* Modified by Rick
		//undo drawing charge bar
		MESSAGE_BEGIN( MSG_ONE, gmsgCharge, NULL, m_pPlayer->pev ); 
			WRITE_BYTE(0);	//amount charged in %
		MESSAGE_END();*/

		m_pPlayer->DrawChargeBar(0); // undo drawing charge bar
		
		return TRUE;
	}
	return FALSE;
}

void CControlledBeam::UpdateBeamJumpStatus()
{	if (!CanBeamJump())
		return;
	if(m_pPlayer->m_fBlinded)return;
	if (m_pPlayer->pev->button & IN_JUMP)
	{	if(!m_fBeamJump)
		{	EnableBeamJump();
		}
		if(m_fBeamJumping)
		{	if (UTIL_WeaponTimeBase() > m_flTimeWeaponIdle)
			{	if(!m_pPlayer->UseKi(m_iKiCost*BEAMJUMP_KI_COST))
				{	DisableBeamJump();					
				}
			}
		}
		else
		{	if(m_pMagic->m_fStopped&&!m_fBeamJumping)
			{	StartBeamJump();
			}
		}		
	}
	else
	{	if(m_fBeamJump)
		{	DisableBeamJump();			
		}
	}
}

void CControlledBeam::StopAiming()
{	m_pMagic->SetFollower(NULL,m_pPlayer);

	//disable powerstruggle bar
	MESSAGE_BEGIN( MSG_ONE, gmsgPowerStruggle, NULL, m_pPlayer->pev ); 
		WRITE_CHAR( 127 );
	MESSAGE_END();
 
	m_pPlayer->m_fPowerStruggling = false;
	m_pPlayer->m_pPowerStruggle = NULL;
	m_pPlayer->m_fBlockStruggleAttacker = false;

	// Edited by Pcjoe, only return slowdown if the player is not ascending
	if(!m_pPlayer->m_fAscending&& !m_pPlayer->m_fMelee && !m_pPlayer->m_fStrangled)
	{	m_pPlayer->m_flSlowDown = 1.0;
	}
	else
	{	// Pcjoe: HACK HACK - Reset sequence after we release a beam
		m_pPlayer->pev->sequence = 0;
		m_pPlayer->m_eAnimationTrigger = PLAYER_NULL;
		m_pPlayer->ResetSequenceInfo();
	}
	m_pPlayer->m_fControl = false;

	if(m_fBeamJumping)
	{	StopBeamJump();
		m_pPlayer->StopFly();
	}
	if (m_fBeamJump)
	{	DisableBeamJump();
	}
	
	m_eAttackState = ATTACK_IDLE;

	//disable control sound
	SilenceBodySounds();

	// Added by Pcjoe, disable model fixing
	if(!m_pPlayer->m_fWallJump)
	{	UTIL_Model_RemoveAll(m_pPlayer->entindex());
	}

	m_pPlayer->DrawChargeBar(0);

	MESSAGE_BEGIN( MSG_ONE, gmsgRemoveEffect, NULL, m_pPlayer->edict() );
		WRITE_BYTE(BEAMBOXCROSSHAIR);				
	MESSAGE_END();
}

CBaseEntity* CControlledBeam::FindFollower()
{
	// create aiming box on closest other beam that is not shot by the player
	CBaseEntity *pEntity = NULL;
	CBaseEntity *pClosests = NULL;
	edict_t		*pEdict = g_engfuncs.pfnPEntityOfEntIndex(1);
	
	ALERT(at_console,"Finding followers\n");

	Vector vecDir;

	if(m_pMagic)
	{
		vecDir = (m_pMagic->pev->origin - m_pPlayer->GetGunPosition());
	}
	else
	{
		UTIL_MakeVectors( m_pPlayer->pev->v_angle );
		//vecDir = gpGlobals->v_forward * 4096;
		vecDir = gpGlobals->v_forward * 10;
	}
	
	Vector vecStart = m_pPlayer->GetGunPosition();
	double flLength = vecDir.Length();
	double flLength2 =flLength*flLength;
	int i=1;
	double flPrevDist;
	if(pEdict)
	{
		for(i=1;i<gpGlobals->maxEntities;i++,pEdict++)
		{
			if ( pEdict->free )	// Not in use
				continue;

			pEntity = CBaseEntity::Instance(pEdict);
			if ( !pEntity )
				continue;
			if(pEntity->Classify() == CLASS_EXPLODINGATTACK)
			{	if(pEntity->pev->owner)
				{
					entvars_t *pevOwner = VARS(pEntity->pev->owner);
					if(pevOwner!=m_pPlayer->pev)
					{
						
						double t = -DotProduct(vecStart-pEntity->pev->origin,vecDir)/flLength2;
						ALERT(at_console,"t: %f\n",t);
						//if(t>0)
						if(t>1)
						{
							double d = CrossProduct(vecDir,vecStart-pEntity->pev->origin).Length()/flLength;
							ALERT(at_console,"d: %f\n",d);
							if(d<MAX_BEAM_AIM_DIST)
							{	if(pClosests)
								{
									if(d<flPrevDist)
									{
										pClosests=pEntity;
										flPrevDist=d;
									}
								}
								else
								{
									pClosests=pEntity;
									flPrevDist=d;
								}
							}
						}
					}
				}
			}
		}
	}
	return pClosests;
}
void CControlledBeam::WeaponIdle( void )
{	
	if (m_eAttackState == ATTACK_CHARGE)
	{	// Added by Pcjoe: Walljump check
		if(!CheckWallJump())
		{	Shoot();
		}
	}

	if(m_eAttackState == ATTACK_AIM)
	{	
		if(m_pMagic)
		{	
			
			
			
			//magic exploded, back to idle mode
			if(m_pMagic->m_fExploded||m_pMagic->m_fReleased)
			{	
				StopAiming();				
				if(m_pMagic->m_fExploded)
				{	m_pMagic->Remove();
				}
				m_pMagic = NULL;				
			}
			//show powerstrugglebar
			else if (m_pMagic->m_fPowerStruggle)
			{	m_pMagic->SetFollower(NULL,m_pPlayer);
				m_pPlayer->m_fPowerStruggling = true;
				m_pPlayer->m_pPowerStruggle = m_pMagic->m_pPowerStruggle;
				
				//release charged up energy
				if (m_iCharge>0)
				{
					/*
					double flTotalDist = 4096;
					if(m_pMagic->m_pPowerStruggle->GetType()==FRONTPS)
					{
						flTotalDist = m_pMagic->m_pPowerStruggle->GetDistance();						
					}
					double flDist = (m_pMagic->pev->origin-m_pPlayer->pev->origin).Length();					
					
					if(flDist<=0)flDist=1;		
					if(flDist>flTotalDist)flDist=flTotalDist;					
					int iDmg = PS_DAMAGERATIO*(1.0-(flDist/flTotalDist)) * GetPowerRatio() * m_iDamage * m_iCharge;
					*/
					
					int iDmg = PS_DAMAGERATIO * GetPowerRatio() * m_iDamage * m_iCharge;
					DelayedDamage *d = new DelayedDamage;
					d->pAttack = m_pMagic;
					d->damage = iDmg;
					d->next = NULL;
					d->speed = 300;
					d->flAddTime = gpGlobals->time;
					m_pMagic->m_pPowerStruggle->AddPSDamage(d);
					//m_pMagic->pev->dmg+=dmg;  
					m_iCharge = 0;
					MESSAGE_BEGIN( MSG_ALL, gmsgAddEffect); 
						WRITE_BYTE(PSBLOB);
						WRITE_SHORT(m_pMagic->entindex());
						WRITE_LONG(iDmg);
						WRITE_LONG(300);
					MESSAGE_END();
					m_pPlayer->DrawChargeBar(0); // disable charge bar
				}
				if (UTIL_WeaponTimeBase() > m_flTimeWeaponIdle)
				{
					MESSAGE_BEGIN( MSG_ONE, gmsgPowerStruggle, NULL, m_pPlayer->pev ); 
						WRITE_CHAR( (int)(m_pMagic->m_flStruggleRatio * 100));	//struggle ratio in % (-100 till 100 %)
					MESSAGE_END();
				}
			}
			//show block struggle bar
			else if (m_pMagic->m_fBlockStruggle)
			{	m_pMagic->SetFollower(NULL,m_pPlayer);
				m_pPlayer->m_fBlockStruggleAttacker = true;
				if (UTIL_WeaponTimeBase() > m_flTimeWeaponIdle)
				{
					MESSAGE_BEGIN( MSG_ONE, gmsgPowerStruggle, NULL, m_pPlayer->pev );
						WRITE_CHAR( (int)(m_pMagic->m_flStruggleRatio * 100));	//struggle ratio in % (-100 till 100 %)
					MESSAGE_END();
				}
			}
			else
			{	m_pMagic->SetFollower(FindFollower(),m_pPlayer);
				m_pPlayer->m_fPowerStruggling = false;
				m_pPlayer->m_pPowerStruggle = NULL;
				m_pPlayer->m_fBlockStruggleAttacker = false;
				UpdateBeamJumpStatus();
		
				//hit by melee, release attack
				if(m_pPlayer->m_fMelee)
				{	m_pMagic->Release();
				}
			}
		}
		else 
		{	StopAiming();			
		}		
	}

	if(m_flTimeWeaponIdle < UTIL_WeaponTimeBase())
	{	m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + CHARGETIME;
	}
}

void CControlledBeam::Shoot()
{	/* Modifed by Rick
	//disable charge bar
	MESSAGE_BEGIN( MSG_ONE, gmsgCharge, NULL, m_pPlayer->pev ); 
		WRITE_BYTE( 0 );	//amount charged in %
	MESSAGE_END();*/

	m_pPlayer->DrawChargeBar(0); // disable charge bar
	
	//silence charge sounds
	SilenceWeaponSounds();
		
	m_pPlayer->m_fCharging = false;

	//charge to low?
	if (m_iCharge<m_iMaxCharge/3)
	{	m_eAttackState=ATTACK_IDLE;
		m_pPlayer->m_flSlowDown = 1.0;

		SilenceVoice();
		m_iCharge=0;
		return;
	}
	m_eAttackState = ATTACK_AIM;	
	if(!FBitSet(m_pPlayer->pev->flags,FL_ONGROUND))
	{	m_pPlayer->StartFly();
	}
	m_pPlayer->m_fControl = true;

	//turn the charging sprite off
	DisableChargeSprite();
		
	//make shoot/control sounds
	EmitShootSound();
	EmitControlSound();

	UTIL_MakeVectors( m_pPlayer->pev->v_angle );
	Vector vecSrc = m_pPlayer->GetGunPosition();

	//do a traceline, to place beamjump beams correctly
	TraceResult tr;
	UTIL_TraceLine(vecSrc, vecSrc + gpGlobals->v_forward * 10,ignore_monsters,ENT(m_pPlayer->pev),&tr);
	vecSrc = vecSrc + gpGlobals->v_forward * 10 * tr.flFraction *0.9;
	Vector vecThrow = gpGlobals->v_forward * m_iSpeed;
	m_pPlayer->m_flSlowDown = 0.0;

	//shoot the magic
	float flSize = (float)m_iCharge/(float)m_iMaxCharge;	
	CreateBeam(vecSrc,vecThrow,flSize);	
	
	// Added by Pcjoe, lock model angles
	UTIL_Model_FixAngles(m_pPlayer->entindex());
	m_iCharge=0;	
}

void CControlledBeam::CreateBeam(Vector vecSrc, Vector vecThrow, float flSize)
{
	m_pMagic = CGuidedAttack::ShootGuidedMagic( m_pPlayer->pev, vecSrc, vecThrow, m_iCharge * m_iDamage * GetPowerRatio(),m_pAttackName ,flSize, m_iExplosionColor,m_sTrail, m_sStartA, m_sStartB, m_sFlareA, m_sFlareB, m_iRadius, m_flRateOfTurn,m_iAttachment,2);
	if(m_pFollower)m_pMagic->SetFollower(m_pFollower,m_pPlayer);
}

void CControlledBeam::Charge()
{	
	//should be done more generic and possibly for all attacks...
	//also, move the m_pFollower onto the shot attack
	CBaseEntity *pEnt = FindFollower();
	if(pEnt!=NULL)
	{
		if(pEnt!=m_pFollower)
		{
			m_pFollower = pEnt;
			MESSAGE_BEGIN( MSG_ONE, gmsgAddEffect, NULL, m_pPlayer->edict() );
				WRITE_BYTE(BEAMBOXCROSSHAIR);								
				WRITE_SHORT(m_pFollower->entindex());				
			MESSAGE_END();
		}
	}
	else
	{
		if(m_pFollower)
		{
			MESSAGE_BEGIN( MSG_ONE, gmsgRemoveEffect, NULL, m_pPlayer->edict() );
				WRITE_BYTE(BEAMBOXCROSSHAIR);				
			MESSAGE_END();
		}
		m_pFollower = NULL;
	}
	
	if(m_eAttackState == ATTACK_AIM)
	{	StopAiming();
	}

	//first charge
	if (m_eAttackState != ATTACK_CHARGE)
	{	m_pPlayer->m_flSlowDown = m_flPlayerSlowdown;
		m_eAttackState = ATTACK_CHARGE;
		m_pPlayer->m_fCharging = true;
		m_iCharge = 0;
		
		//make start charge sound
		EmitChargeSound();
	}
	
	//charge it
	if (m_iCharge < m_iMaxCharge)
	{	// Check we have some ammo
		if (!m_pPlayer->UseKi(m_iKiCost))
		{	Shoot();
			return;
		}		
	
		if (m_iCharge == m_iMaxCharge/3)
		{	EnableChargeSprite();
		}
		
		if (m_iCharge > m_iMaxCharge/3)
		{	GrowChargeSprite();
		}

		m_iCharge++;

		/* Modifed by Rick
		MESSAGE_BEGIN( MSG_ONE, gmsgCharge, NULL, m_pPlayer->pev ); 
			WRITE_BYTE( m_iCharge*100/m_iMaxCharge);	//amount charged in %
		MESSAGE_END();*/
		
		m_pPlayer->DrawChargeBar(m_iCharge*100/m_iMaxCharge); // Update charge bar
		

		
	}
	/* auto-shoot removed
	else
	{	Shoot();
	}
	*/
}

void CControlledBeam::PrimaryAttack( void )
{	
	m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + CHARGETIME;	
	//don't fire a new one while aiming a controlled beam
	if (m_eAttackState == ATTACK_AIM  && m_pMagic)
	{	if(m_pMagic->m_fExploded || m_pMagic->m_fReleased)
		{	
			StopAiming();
			if(m_pMagic->m_fExploded)
			{	m_pMagic->Remove();
			}
			m_pMagic = NULL;
		}
		else if (m_pMagic->m_fBlockStruggle)
		{	if (m_pPlayer->UseKi(m_iKiCost * PS_KIRATIO))
			{	m_pMagic->SetDamage(m_pMagic->pev->dmg + GetPowerRatio() * m_iDamage);
			}
			m_pPlayer->m_fBlockStruggleAttacker = true;
			MESSAGE_BEGIN( MSG_ONE, gmsgPowerStruggle, NULL, m_pPlayer->pev );
				WRITE_CHAR( (int)(m_pMagic->m_flStruggleRatio * 100));	//struggle ratio in % (-100 till 100 %)
			MESSAGE_END();			
		}
		else if (m_pMagic->m_fPowerStruggle)
		{	if (m_iCharge<m_iMaxCharge)
			{	if (m_pPlayer->UseKi(m_iKiCost * PS_KIRATIO))
				{	// reduce our ammo
					//m_pMagic->SetDamage(m_pMagic->pev->dmg + GetPowerRatio() * m_iDamage);
					m_iCharge++;
					m_pPlayer->DrawChargeBar(101+m_iCharge*100/m_iMaxCharge);
				}
			}
			m_pPlayer->m_fPowerStruggling = true;
			m_pPlayer->m_pPowerStruggle = m_pMagic->m_pPowerStruggle;
			
			MESSAGE_BEGIN( MSG_ONE, gmsgPowerStruggle, NULL, m_pPlayer->pev );
				WRITE_CHAR( (int)(m_pMagic->m_flStruggleRatio * 100));	//struggle ratio in % (-100 till 100 %)
			MESSAGE_END(); 
		}
		else
		{// Edited by Pcjoe
			/*
			m_pPlayer->m_fPowerStruggling = false;
			m_pPlayer->m_fBlockStruggleAttacker = false;
			UpdateBeamJumpStatus();
		
			//hit by melee, release attack
			if(m_pPlayer->m_fMelee)
			{	m_pMagic->Release();
			}
			*/
			WeaponIdle();
			m_flNextPrimaryAttack = UTIL_WeaponTimeBase();
			return;
		}
	}
	else
	{	
		/*
		CBaseEntity* pFollower = FindFollower();
		if(pFollower!=m_pFollower)		
		{
			if(pFollower)
			{
				ALERT(at_console,"Following attack\n");
				MESSAGE_BEGIN( MSG_ONE, gmsgAddEffect, NULL, m_pPlayer->edict() );
					WRITE_BYTE(BEAMBOXCROSSHAIR);								
					WRITE_SHORT(pFollower->entindex());				
				MESSAGE_END();			
			}
			else
			{
				MESSAGE_BEGIN( MSG_ONE, gmsgRemoveEffect, NULL, m_pPlayer->edict() );
					WRITE_BYTE(BEAMBOXCROSSHAIR);				
				MESSAGE_END();
			}
		}
		m_pFollower = pFollower;
		*/
		Charge();			
		m_flNextSecondaryAttack = m_flNextPrimaryAttack;
	}	
}

void CControlledBeam::DetonateAttack()
{	StopAiming();
	if(m_pMagic)
	{	if(!m_pMagic->m_fExploded && !m_pMagic->m_fReleased)
		{	m_pMagic->Explode();			
		}
		if(m_pMagic->m_fExploded)
		{	m_pMagic->Remove();
		}
		m_pMagic = NULL;
	}	
}

void CControlledBeam::ReleaseAttack()
{
	if(m_pMagic)
	{	m_pMagic->Release();		
		m_pMagic = NULL;
	}
}

void CControlledBeam::SecondaryAttack( void )
{	
	if(m_pMagic && m_eAttackState == ATTACK_AIM)
	{	if(!m_pMagic->m_fPowerStruggle && !m_pMagic->m_fBlockStruggle)
		{	DetonateAttack();
		}
		else if (m_pMagic->m_fPowerStruggle && m_pMagic->m_pPowerStruggle->m_pAttack->side==0)
		{
			//sideways ps, we can release
			ReleaseAttack();
		}
	}
	m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + CHARGETIME;
}

void CControlledBeam::EmitControlSound()
{	EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_BODY, "weapons/beamfire.wav", 1.0, ATTN_NORM);
}

BOOL CControlledBeam::CanHolster()
{	if (m_pMagic && m_eAttackState == ATTACK_AIM)
	{	if( (m_pMagic->m_fPowerStruggle||m_pMagic->m_fBlockStruggle) &&!m_pMagic->m_fReleased)
		{	return false;
		}	
	}
	return true;	
}