#include "extdll.h" 
#include "decals.h" 
#include "util.h" 
#include "cbase.h" 
#include "monsters.h" 
#include "weapons.h" 
#include "nodes.h" 
#include "effects.h"
#include "soundent.h" 
#include "shake.h" 
#include "gamerules.h" 
#include "studio.h"
#include "envmodel.h"

#define ENVMODEL_MINSIZE 20
#define ENVMODEL_MAXSIZE 100
#define ENVMODEL_SIZERANGE (ENVMODEL_MAXSIZE-ENVMODEL_MINSIZE)
#define ENVMODEL_MAXSPEED 2000
#define ENVMODEL_MINSPEED 1500
#define ENVMODEL_SPEEDRANGE (ENVMODEL_MAXSPEED-ENVMODEL_MINSPEED)
#define ENVMODEL_MAXDMG 60
#define ENVMODEL_MINDMG 21
#define ENVMODEL_DMGRANGE (ENVMODEL_MAXDMG-ENVMODEL_MINDMG)

LINK_ENTITY_TO_CLASS(env_model,CEnvModel);
extern cvar_t	envrespawn;

void CEnvModel::KeyValue( KeyValueData* pkvd )
{
	if (FStrEq(pkvd->szKeyName, "boundingbox"))
	{
		int iVal = atoi(pkvd->szValue);
		if (iVal==1)
		{
			pev->solid = SOLID_NOT;			
			m_fBoundingBox = false;
		}
		else
		{
			pev->solid = SOLID_BBOX;			
			m_fBoundingBox = true;
		}
		pkvd->fHandled = TRUE;
	}
	else
	{
		CBaseMonster::KeyValue(pkvd);
	}
}

void CEnvModel::Spawn()
{	Precache();
	SET_MODEL( ENT(pev), STRING(pev->model) );
	m_vecSpawnAngles = pev->angles;
	pev->max_health = pev->health;	
	if(pev->health == -1)
	{	pev->takedamage = DAMAGE_NO;
	}
	else
	{
		pev->takedamage = DAMAGE_YES;
	}
	m_vecStart = pev->origin;
	SetModelCollisionBox();	
	m_vecBBMin = pev->mins;
	m_vecBBMax = pev->maxs;
	m_flSize = (pev->maxs.x-pev->mins.x)*(pev->maxs.y-pev->mins.y)*(pev->maxs.z-pev->mins.z);
	m_flSize = pow(m_flSize,float(1.0/3.0));
	if(m_flSize<ENVMODEL_MINSIZE)
		m_flSize = ENVMODEL_MINSIZE;
	if(m_flSize>ENVMODEL_MAXSIZE)
		m_flSize = ENVMODEL_MAXSIZE;

	m_flMaxZ = pev->maxs.z;
	m_flMinZ = pev->mins.z;
	m_flMinXY = min(pev->mins.x,pev->mins.y);
	m_flMaxXY = max(pev->maxs.x,pev->maxs.y);
	//SetBoundingBox();
	Reset();	
}	

void CEnvModel::Reset()
{
	pev->velocity=Vector(0,0,0);
	pev->angles=m_vecSpawnAngles;
	//pev->angles=Vector(0,0,0);
	
	pev->avelocity=Vector(0,0,0);
	pev->owner = NULL;
	pev->health = pev->max_health;	
	pev->movetype = MOVETYPE_FLY;	
	pev->gravity=0;	
	UTIL_SetOrigin( pev, pev->origin );	
	pev->effects &= ~EF_NODRAW;	
	
		
	//pev->movetype = MOVETYPE_NONE;
	//pev->solid = SOLID_BBOX;
	//pev->classname = MAKE_STRING( "env_model" );
	
	SetIdleAnimation();
	
	SetTouch(&CEnvModel::EnvTouch);
	SetThink(&CEnvModel::AnimateThink);
	m_fLift = false;
		
}

void CEnvModel::RespawnThink()
{	
	CBaseEntity *pList[256];
	int iCount = UTIL_EntitiesInBox( pList, 256, m_vecStart - pev->mins, m_vecStart + pev->maxs, NULL);
	int i=0;
	int fPlaceIt=true;
	for(i=0;i<iCount;i++)
	{
		if(pList[i]->IsEnvModel())
		{
			CEnvModel *pEnv=(CEnvModel*)pList[i];
			if(pEnv->m_fLift)
			{
				fPlaceIt=false;
				break;
			}						
		}
		else
		{
			fPlaceIt=false;
			break;
		}
	}

	if(fPlaceIt)
	{
		pev->origin = m_vecStart;
		Reset();
		if(m_fBoundingBox)
		{
			pev->solid = SOLID_BBOX;
			UTIL_SetSize(pev,m_vecBBMin,m_vecBBMax);
		}
		ALERT(at_console,"Solid: %d \t Movetype: %d	Owner: %d\n",pev->solid,pev->movetype,pev->owner);
	}
	else
	{
		pev->nextthink = gpGlobals->time+1;
	}
}

void CEnvModel::ExplodeTouch( CBaseEntity *pOther )
{
	entvars_t *pevOwner;
	if ( pev->owner )
		pevOwner = VARS( pev->owner );
	else
		pevOwner = NULL;	
	::RadiusDamage( pev->origin, pev, pevOwner, pev->dmg, ENVMODEL_RADIUS * pev->dmg, CLASS_NONE, DMG_MAGIC );
	
	//PLACEHOLDER. We should add real particle rock explosions here later on
	// Edited by Pcjoe, Fixed the explosion going through the ground problem
	TraceResult tr;
	Vector vecBall = pev->velocity.Normalize() * ENVMODEL_RADIUS;
	Vector vecVelocity = pev->velocity.Normalize();

	// Move it back a bit so the origin wont be in SOLID
	Vector vecOrigin = pev->origin + vecVelocity * -20;
	UTIL_TraceLine ( vecOrigin, vecOrigin + vecBall + vecVelocity * 30,  ignore_monsters, ENT(pev), &tr);
	Vector vecSrc = tr.vecEndPos;

	if (pev->flags | FL_ONGROUND && tr.flFraction < 1.0)
	{	MESSAGE_BEGIN( MSG_ALL, SVC_TEMPENTITY);
			WRITE_BYTE(TE_BREAKMODEL);					// box of models or sprites
			WRITE_COORD( vecSrc.x );					// coord, coord, coord (position)
			WRITE_COORD( vecSrc.y );
			WRITE_COORD( vecSrc.z );
			WRITE_COORD( 50 );							// coord, coord, coord (size)
			WRITE_COORD( 50 );
			WRITE_COORD( 50 );
			WRITE_COORD( tr.vecPlaneNormal.x * 200);	// coord, coord, coord (velocity)
			WRITE_COORD( tr.vecPlaneNormal.y * 200);
			WRITE_COORD( tr.vecPlaneNormal.z * 200);
			WRITE_BYTE(30);								// byte (random velocity in 10's)
			WRITE_SHORT(m_sRockGibs);					// short (sprite or model index)
			WRITE_BYTE(20);								// byte (count)
			WRITE_BYTE(50);								// byte (life in 0.1 secs)
			WRITE_BYTE(0);								// byte (flags)
		MESSAGE_END();
	}
	pev->effects |= EF_NODRAW;
	pev->solid=SOLID_NOT;	
	pev->nextthink = gpGlobals->time+envrespawn.value;
	SetThink(&CEnvModel::RespawnThink);
	ALERT(at_console,"Boom");
	//UTIL_Remove(this);
}

void CEnvModel::EnvTouch( CBaseEntity *pOther )
{
	if(pOther->IsPlayer())
	{
		int iSeq = LookupSequence( "touch" );
		if (iSeq!=-1)
		{	pev->gaitsequence = 0;
			pev->frame = 0;
			pev->sequence = iSeq;
			ResetSequenceInfo ( );
			pev->nextthink=gpGlobals->time+GetAnimTime(pev->sequence);
			SetThink(&CEnvModel::AnimateThink);			
		}
	}	
}

int CEnvModel::TakeDamage(entvars_t *pevInflictor,entvars_t *pevAttacker, float flDamage, int bitsDamageType)
{
	int iSeq = LookupSequence( "explosion" );
	if (iSeq != -1)
	{ 
		pev->gaitsequence = 0;
		pev->frame = 0;	
		pev->sequence = iSeq;
		ResetSequenceInfo ( );
		SetThink(&CEnvModel::AnimateThink);
		pev->nextthink=gpGlobals->time+1;
	}
	//int iTakeDamage = CBaseMonster::TakeDamage(pevInflictor,pevAttacker,flDamage,bitsDamageType);
	pev->health -=flDamage;
	if ( pev->health <= 0 )
	{
		pev->effects |= EF_NODRAW;
		pev->solid=SOLID_NOT;	
		pev->nextthink = gpGlobals->time+envrespawn.value;
		SetThink(&CEnvModel::RespawnThink);
	}
	return flDamage;
}

void CEnvModel::SetIdleAnimation()
{
	int iSeqIdle = LookupSequence( "idle" );
	int iSeqIdle2 = LookupSequence( "idle2" );

	if (iSeqIdle!=-1)
	{
		pev->sequence = iSeqIdle;
		if (iSeqIdle2!=-1)
		{	if(RANDOM_FLOAT(0,1)>0.5)
			{
				pev->sequence = iSeqIdle2;
			}
		}
		pev->gaitsequence = 0;
		pev->frame = 0;	
		ResetSequenceInfo ( );	
	}
	else
	{
		ALERT(at_console,"No idle animation found\n");
	}
}

void CEnvModel::AnimateThink()
{	SetIdleAnimation();
}

void CEnvModel::Precache()
{	PRECACHE_MODEL( (char*)STRING(pev->model) );	
	m_sRockGibs = PRECACHE_MODEL("models/rockgibs.mdl");
}

void CEnvModel::Shoot(Vector direction, float charge, float power)
{
	pev->avelocity=Vector(0,0,0);
	pev->solid=SOLID_TRIGGER;
	float flRelSize = (m_flSize-ENVMODEL_MINSIZE)/ENVMODEL_SIZERANGE;
	float flSpeed = ENVMODEL_MAXSPEED-(ENVMODEL_SPEEDRANGE*flRelSize);
	float flDamage = ENVMODEL_MINDMG+ENVMODEL_DMGRANGE*flRelSize;
	pev->velocity = flSpeed * direction;
	pev->movetype = MOVETYPE_FLY;
	pev->dmg = charge*flDamage*power;
	
	//pev->angles = UTIL_VecToAngles(pev->velocity);
	SetBoundingBox();
	SetThink(NULL);
	SetTouch(&CEnvModel::ExplodeTouch);
	//m_fLift = false;
}

void CEnvModel::SetBoundingBox()
{
	float flRadius = m_flMaxZ - m_flMinZ;
	float flYawRad = 0.5 * M_PI + pev->angles.y*2.0*M_PI/360.0;
	float flX = -flRadius*cos(flYawRad);
	float flY = -flRadius*sin(flYawRad);
	
	float flXmin=min(m_flMinZ,m_flMinZ+flX); 
	float flYmin=min(m_flMinZ,m_flMinZ+flY); 
	float flXmax=max(m_flMinZ,m_flMinZ+flX); 
	float flYmax=max(m_flMinZ,m_flMinZ+flY); 
	
	UTIL_SetSize(pev,Vector(flXmin,flYmin,m_flMinXY),Vector(flXmax,flYmax,m_flMaxXY));
}

void CEnvModel::Lift(entvars_t *pevOwner,float flRaiseSpeed,float flRaiseTime)
{
	ALERT(at_console,"Size %f\n",m_flSize);
	pev->velocity = Vector(0,0,flRaiseSpeed);
	
	//no clipping for easy rotate
	SetTouch(NULL);
	pev->movetype = MOVETYPE_NOCLIP;
	pev->solid = SOLID_NOT;
	
	//m_pEnvModel->pev->avelocity = m_pPlayer->pev->v_angle * 1.0/flTime;
	//m_pEnvModel->pev->avelocity = Vector(90,0,0) * 1.0/flTime;
	/*
	UTIL_MakeVectors( pevOwner->v_angle );
	Vector vecAiming = gpGlobals->v_forward;
	Vector vecSrc = pevOwner->origin + pevOwner->view_ofs;
	TraceResult tr;
	UTIL_TraceLine (vecSrc,vecSrc + vecAiming * 8192, dont_ignore_monsters, ENT(pev), &tr );
	
	vecAiming = tr.vecEndPos - pev->origin;
	vecAiming = vecAiming.Normalize();
	Vector vecAngles = UTIL_VecToAngles(vecAiming);
	*/
	Vector prevAngles;	
	Vector vecAngles = pevOwner->v_angle;


	//Euler angles are evil...
	prevAngles.x = vecAngles.x;			//PITCH
	prevAngles.y = vecAngles.y-90;		//YAW
	prevAngles.z = 90;					//ROLL

	pev->gravity = 0.0000001;
	pev->owner = ENT(pevOwner);
	SetThink(&CEnvModel::RotateThink);

	//schmoove rotate
	pev->avelocity = (prevAngles - pev->angles)/flRaiseTime;
	pev->nextthink = gpGlobals->time+flRaiseTime;
	m_fLift = true;
}

void CEnvModel::Drop()
{
	/*
	SetBoundingBox();	
	pev->velocity = Vector(0,0,0);
	pev->avelocity = Vector(0,0,0);
	pev->owner = NULL;
	pev->gravity = 9.8;
	pev->movetype = MOVETYPE_TOSS;
	pev->solid = SOLID_BBOX;
	SetThink(NULL);
	m_fLift = false;
	*/
	pev->effects |= EF_NODRAW;
	pev->solid = SOLID_NOT;
	pev->nextthink = gpGlobals->time+envrespawn.value;
	SetThink(&CEnvModel::RespawnThink);
}

void CEnvModel::RotateThink()
{
	entvars_t *pevOwner;
	if (pev->owner)
	{	pevOwner = VARS(pev->owner);
	}
	else
	{	ALERT(at_console,"Invalid owner for envmodel");
		return;
	}
	/*	
	UTIL_MakeVectors( pevOwner->v_angle );
	Vector vecAiming = gpGlobals->v_forward;
	Vector vecSrc = pevOwner->origin + pevOwner->view_ofs;
	TraceResult tr;
	UTIL_TraceLine (vecSrc,vecSrc + vecAiming * 8192, dont_ignore_monsters, ENT(pev), &tr );
	
	vecAiming = tr.vecEndPos - pev->origin;
	vecAiming = vecAiming.Normalize();
	Vector vecAngles = UTIL_VecToAngles(vecAiming);
	*/
	Vector vecAngles = pevOwner->v_angle;
	//ALERT(at_console,"v_angle %f,%f,%f\n",vecAngles.x,vecAngles.y,vecAngles.z);

	Vector prefAngles;	

	//Euler angles are evil...
	prefAngles.x = vecAngles.x;			//PITCH
	//prefAngles.x = 0;
	prefAngles.y = vecAngles.y-90;		//YAW
	//if(prefAngles.y<0)prefAngles.y+=360;
	prefAngles.z = 90;					//ROLL
	//ALERT(at_console,"Angles %f,%f,%f\n",prefAngles.x,prefAngles.y,prefAngles.z);
	
	//schmoove rotate
	pev->angles = prefAngles;
	//pev->avelocity = (prefAngles - pev->angles)/0.1;
	pev->nextthink = gpGlobals->time + 0.01;	
}
