#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 "beamattack.h"
#include "kiattack.h"

#define MAX_SHOTS 8
#define SCATTERSHOT_SIZE 25
#define TRAIL_LENGTH 40
#define SCATTERSHOT_SPEED			 750
#define SCATTERSHOT_CONTRACT_SPEED	1900
#define SCATTERSHOT_STOPTIME		1.5
#define SCATTERSHOT_KI_COST			20
#define SCATTERSHOT_DAMAGE			10
#define SCATTERSHOT_RADIUS			100

extern int gmsgFlare;
/**
 * Scatter shot
 * @version 18-5-2002
 * @author Herwin 'harSens' van Welbergen
 */
class CScatterShotAttack:public CKiAttack
{	
public:
	/**
	 * Create a scattershot attack 
	 * @param entvars_t *pevOwner: Monster/Player who shoots it
	 * @param Vector vecStart: Origin vector of the attack
	 * @param Vector vecVelocity: Velocity of the attack
	 * @param damage: the amount of damage to be done
	 * @param CScatterShotAttack *: the newly created attack
	 */
	static CScatterShotAttack* ShootScatterShotAttack( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, int damage, short flarea, short flareb, short trail);
	
	/**
	 * Set the contract target
	 * @param target the target
	 */
	void SetTarget(Vector target);

	/**
	 * for contracts at the crosshair
	 */
	//void EXPORT ContractThink ( void );

	/**
	 * Decelerates attack to a stop
	 */
	void EXPORT StopThink ( void );

	/**
	 * Stops the scattershot
	 */
	void Stop();

	//Vector vecContract;					//point to contract to
	//float m_flDistanceToTarget;
	int m_fContracting;
protected:
	/**
	 * Gets the explosion radius
	 * @param return int: the explosion radius
	 */
	virtual float GetExplosionRadius(){return SCATTERSHOT_RADIUS;}	
private:
	float m_flDecelTime;
	Vector m_vecDir;
};

void CScatterShotAttack :: SetTarget(Vector target)
{
	m_flSpeed = SCATTERSHOT_CONTRACT_SPEED;
	Vector vecDir=target-pev->origin;
	pev->velocity = m_flSpeed*vecDir.Normalize();
	pev->angles = UTIL_VecToAngles( pev->velocity );
	m_fContracting=1;
}

void CScatterShotAttack::Stop()
{
	m_flDecelTime = gpGlobals->time;
	m_vecDir = pev->velocity.Normalize();
	SetThink(&CScatterShotAttack::StopThink);
	pev->nextthink = gpGlobals->time;
}

void CScatterShotAttack::StopThink()
{
	float flTimeDiff = (gpGlobals->time-m_flDecelTime);
	float flSpeed = SCATTERSHOT_SPEED - (flTimeDiff*SCATTERSHOT_SPEED)/SCATTERSHOT_STOPTIME;
	if(flSpeed>0)
	{
		pev->velocity = m_vecDir * flSpeed;		
	}
	else
	{
		pev->velocity=Vector(0,0,0);
		SetThink(&CScatterShotAttack::NormalThink);
	}
	NormalThink();
	pev->nextthink = gpGlobals->time+0.01;
}

/*
void CScatterShotAttack :: ContractThink( void  )
{	Vector vecOldVelocity = pev->velocity;
		
	//go to contraction point
	Vector vecDir = vecContract - pev->origin;
	float flDist = vecDir.Length();
	//explode near contraction point
	if (flDist>m_flDistanceToTarget)
	{	Explode();
		return;
	}

	vecDir = vecDir.Normalize();
	if (vecOldVelocity.Length() > m_flSpeed)
	{	pev->velocity = vecDir * m_flSpeed;
	}
	else
	{	pev->velocity = vecDir * m_flSpeed * 0.7 + vecOldVelocity * 0.3;
	}
	pev->angles = UTIL_VecToAngles( pev->velocity );
	
	DrawBubbles();//call normal think function
	m_flDistanceToTarget = flDist;
	pev->nextthink = gpGlobals->time + 0.3;
}*/

CScatterShotAttack* CScatterShotAttack::ShootScatterShotAttack( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, int damage, short flarea, short flareb, short trail)
{	CScatterShotAttack *pKiAttack = GetClassPtr( (CScatterShotAttack *)NULL );
	pKiAttack->Spawn();	
	
	pKiAttack->pev->movetype = MOVETYPE_FLY;
	pKiAttack->pev->solid = SOLID_TRIGGER;	//touches everything

	//set model
	SET_MODEL(ENT(pKiAttack->pev), "sprites/null.spr");

	pKiAttack->SetDamage(damage);

	
	float flSize = SCATTERSHOT_SIZE;
	float flAttackSize = flSize * 0.25;	//compensate for sprites empty space
	UTIL_SetOrigin(pKiAttack->pev,vecStart + flAttackSize * vecVelocity.Normalize());
	UTIL_SetSize(pKiAttack->pev, Vector(-flAttackSize, -flAttackSize, -flAttackSize), Vector(flAttackSize, flAttackSize, flAttackSize));

	pKiAttack->pev->velocity = vecVelocity;
	pKiAttack->m_flSpeed = vecVelocity.Length();
	pKiAttack->pev->angles = UTIL_VecToAngles(pKiAttack->pev->velocity);
	pKiAttack->pev->owner = ENT(pevOwner);
	pKiAttack->pev->classname = MAKE_STRING( "scattershot" );

	MESSAGE_BEGIN( MSG_ALL, gmsgFlare, NULL );
		WRITE_LONG(pKiAttack->entindex());		//end entity
		WRITE_SHORT(flarea);					//start attachment
		WRITE_SHORT(flareb);					//start sprites
		WRITE_SHORT( flSize * 0.1);				//size in .1's
		WRITE_SHORT(trail);						//trail sprite
		WRITE_SHORT(TRAIL_LENGTH);				//trail length
		WRITE_COORD(vecStart.x);				//start pos
		WRITE_COORD(vecStart.y);
		WRITE_COORD(vecStart.z);
	MESSAGE_END();

	pKiAttack->SetTouch( &CScatterShotAttack::ExplodeTouch );

	// make monsters/players afraid of it while in the air
	pKiAttack->SetThink( &CScatterShotAttack::NormalThink );
	pKiAttack->pev->nextthink = gpGlobals->time;
	pKiAttack->m_fContracting = false;
	pKiAttack->m_fRemoveAfterExplode = false;
	
	return pKiAttack;	
}

//--------------------------------------------------------------
/**
 * Scattershot
 * @version 18-5-2002
 * @author Herwin 'harSens' van Welbergen
 */
class CScatterShot:public CBasePlayerWeapon
{	
public:
	/**
	 * Init function
	 */
	void Spawn( void );

	/**
	 * Precaches sprites and sounds
	 */
	void Precache( void );

	/**
	 * Get kiblast slot
	 * return int: the slot, is 7 for scattershot
	 */
	int iItemSlot( void ) { return 7; }

	/**
	 * Get kiblast item info
	 * @param ItemInfo *p: The ItemInfo gets stored here
	 * @return int: Always returns 1 (why???)
	 */
	int GetItemInfo(ItemInfo *p);

	/**
	 * Cleans the shotlist
	 */
	void WeaponIdle(void);

	/**
	 * Shoots kiblasts
	 */
	void PrimaryAttack( void );

	/**
	 * Contracts kiblasts
	 */
	void SecondaryAttack(void);

	/**
	 * Called when scattershot is selected as weapon
	 */
	BOOL Deploy( void );

	/**
	 * Holsters scattershot
	 */
	void Holster(int skiplocal = 0);
		
private:
	/**
	 * Adds a shot to the list
	 * @param CScatterShotAttack *attack: the attack to add
	 */
	void AddShotEntry(CScatterShotAttack *attack);
	
	/**
	 * Keeps the shotlist up to date
	 */
	void CleanUpShotList();

	/**
	 * Makes the shot free for removal.
	 */
	void FreeShot(CScatterShotAttack *attack);

	CScatterShotAttack * m_pShotList[MAX_SHOTS];
	int m_iShots;
	enum{IDLE, SHOOTING, SHOOT_STOP, STOPPED} m_eStatus;
	short m_sFlareA,m_sFlareB,m_sTrail;
};
LINK_ENTITY_TO_CLASS( weapon_scattershot, CScatterShot );

void CScatterShot::Spawn( void )
{	Precache();
	m_iId = WEAPON_SCATTERSHOT;
	m_iDefaultAmmo = 0;
	m_iClip = -1;

	FallInit();// get ready to fall down.
}

void CScatterShot::Precache( void )
{	m_sFlareA = PRECACHE_MODEL("sprites/kiblasta.spr");
	m_sFlareB = PRECACHE_MODEL("sprites/kiblastb.spr");
	m_sTrail = PRECACHE_MODEL("sprites/kiblasttrail.spr");
	PRECACHE_SOUND("weapons/kiblast.wav");
	PRECACHE_SOUND("weapons/kiplosion.wav");
}

int CScatterShot::GetItemInfo(ItemInfo *p)
{	p->pszName = STRING(pev->classname);
	p->pszAmmo1 = "ki";
	p->iMaxAmmo1 = -1;	//max ammo is weapon independent
	p->pszAmmo2 = NULL;
	p->iMaxAmmo2 = -1;
	p->iMaxClip = WEAPON_NOCLIP;
	p->iSlot = 6;
	p->iPosition = 3;
	p->iId = m_iId = WEAPON_SCATTERSHOT;
	p->iWeight = SCATTERSHOT_WEIGHT;
	p->iFlags = 0;
	return 1;
}

BOOL CScatterShot::Deploy( void )
{	m_iShots = 0;
	m_eStatus = IDLE;
	return DefaultDeploy( "scattershot" );
}

void CScatterShot::Holster(int skiplocal)
{	for(int i=0;i<m_iShots;i++)
	{
		m_pShotList[i]->Remove();
		//FreeShot(m_pShotList[i]);
	}	
	m_pPlayer->StopAniTrigger(PLAYER_ATTACK1);
	m_pPlayer->StopAniTrigger(PLAYER_ATTACK2);		
}

void CScatterShot::FreeShot(CScatterShotAttack *attack)
{
	if(attack->m_fExploded)
	{
		attack->Remove();
	}
	else
	{	attack->m_fRemoveAfterExplode = true;
	}
}

void CScatterShot::CleanUpShotList()
{	int i=0;
	while (i<m_iShots)
	{	if(m_pShotList[i]->m_fExploded)
		{	m_pShotList[i]->Remove();
			m_iShots--;
			m_pShotList[i]=m_pShotList[m_iShots];
		}
		else
		{	i++;
		}
	}
}

void CScatterShot::AddShotEntry(CScatterShotAttack *attack)
{	m_pShotList[m_iShots] = attack;
	m_iShots++;	
}


void CScatterShot::PrimaryAttack( void )
{	
	if(m_eStatus==IDLE||m_eStatus==SHOOTING)
	{
		m_eStatus = SHOOTING;
		CleanUpShotList();
		//limit shots
		if (m_iShots>=MAX_SHOTS)
			return;

		// Check we have some ammo
		if (!m_pPlayer->UseKi(SCATTERSHOT_KI_COST))
		{	return;
		}
		
		//emit sound
		EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/kiblast.wav", 1.0, ATTN_NORM);

		//shoot it
		int iRight = RANDOM_LONG(-10,10) * 4;
		int iUp = RANDOM_LONG(-10,10) * 4;
		
		//choose shoot animation, shoot origin
		UTIL_MakeVectors( m_pPlayer->pev->v_angle );
		Vector vecSrc;
		
		vecSrc = m_pPlayer->GetGunPosition() + gpGlobals->v_forward * 10;
		m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
			
		Vector vecDir = gpGlobals->v_forward * 200 + gpGlobals->v_right * iRight + gpGlobals->v_up * iUp;
		Vector vecThrow = SCATTERSHOT_SPEED * vecDir.Normalize();
		AddShotEntry(CScatterShotAttack::ShootScatterShotAttack( m_pPlayer->pev, vecSrc, vecThrow, SCATTERSHOT_DAMAGE * GetPowerRatio(),m_sFlareA,m_sFlareB,m_sTrail));
		m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.3;
	}
	else if(m_eStatus==SHOOT_STOP)
	{
		CleanUpShotList();
		for(int i=0;i<m_iShots;i++)
		{	if(!m_pShotList[i]->m_fBlockStruggle && !m_pShotList[i]->m_fPowerStruggle)
			{	
				//ALERT(at_console,"Stopping\n");
				m_pShotList[i]->Stop();
			}			
		}
		m_eStatus = STOPPED;
	}
}

void CScatterShot::WeaponIdle()
{	if(m_eStatus==SHOOTING)
	{
		m_eStatus = SHOOT_STOP;
	}
	CleanUpShotList();
}

void CScatterShot::SecondaryAttack( void )
{	CleanUpShotList();
	UTIL_MakeVectors(m_pPlayer->pev->v_angle);
	Vector vecSrc = m_pPlayer->GetGunPosition();
	Vector vecAiming = gpGlobals->v_forward;
	TraceResult tr;
	UTIL_TraceLine ( vecSrc, vecSrc + vecAiming * 8192, dont_ignore_monsters, pev->owner, &tr );
		
	for(int i=0;i<m_iShots;i++)
	{	if (!m_pShotList[i]->m_fContracting)
		{	if(!m_pShotList[i]->m_fBlockStruggle && !m_pShotList[i]->m_fPowerStruggle)
			{	
				ALERT(at_console,"Contracting\n");
				m_pShotList[i]->SetTarget(tr.vecEndPos);								
			}
			FreeShot(m_pShotList[i]);
		}				
	}
	m_iShots = 0;
	m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.3;
	m_eStatus = IDLE;
}