/*********************************************************************//**
*	BasePhysics
*	Abstraktni trida definuje entity, na ktere se vztahuji fyzikalni zakony,
*	persneji receno pouzivaji dynamicky model.
*	
*	author: Michal Jirous
*	date: 23.04.2009
*	file: ent_basephysics.cpp
**********************************************************************/

#include "ent_basephysics.h"
#include "level_bspfile_def.h"
#include "ctrl_var_definition.h"


variable *g_Gravity = NULL;
variable *g_FrictionNormalGround = NULL;

CBasePhysics::CBasePhysics() : CBaseEntity()
{
	m_iHullType = HULL_PLAYER;
	m_iWaterLevel = NOT_IN_WATER;
	m_bIsInAir = false;

	if( !g_Gravity )
		g_Gravity = gamevarsLibrary::getVariable( vardef::GRAVITY_NAME );
	if( !g_FrictionNormalGround )
		g_FrictionNormalGround = gamevarsLibrary::getVariable( vardef::FRICTION_NAME );

	m_TotalFriction = 0.0f;
	m_iProperties = ENT_PARM_DYNAMIC_MODEL_USAGE |  ENT_PARM_COLLIDING |  ENT_PARM_RENDER |  ENT_PARM_RUNNABLE |  ENT_PARM_SHOOTABLE;
//
}

void CBasePhysics::createModel( int hull, bool originIsUp )
{
	if( !isInRange( hull, HULL_PLAYER, HULL_MODEL64 ) )
	{
		hull = HULL_PLAYER;
	}

	if( originIsUp )
		m_Model.createLinkedBlock( origin, HULL_HEIGHTS[hull], HULL_WIDTHS[hull]/2.0f );
	else
	{
		origin += Point(0,0,HULL_HEIGHTS[hull]);
		m_Model.createLinkedBlock( origin, HULL_HEIGHTS[hull],HULL_WIDTHS[hull]/2.0f);
	}
}

bool CBasePhysics::collisionDetection( CollisionData &collData )
{
	return m_Model.collisionDetection( collData );
}
	

bool CBasePhysics::rayTrace( RayTraceData &rayData )
{
	float distance;
	Point result;
	if( m_Model.m_StaticBounds.lineIntersectDistance( rayData.ray.m_vecDirection, rayData.ray, result, distance ) )
	{
		if( distance > 0 && distance < rayData.distance )
		{
			rayData.distance = distance;
			rayData.intersection = result;
			rayData.target = this;
			return true;
		}
	}
	return false;
}

void CBasePhysics::onCollide_Initiator_this( CollisionData &collData, bool ground_normal )
{
	if( ground_normal && collData.face )
	{
		Face *pFace = (Face*)collData.face;

		pFace->getLightColor( (float*)&rendercolor );
		if( pFace->pTexture && pFace->pTexture->m_pTextureElement )
			m_GroundMaterial = pFace->pTexture->m_pTextureElement->material;
	}

}

void CBasePhysics::restart()
{
	CBaseEntity::restart();
	teleport( m_Store_Position-origin, false );
	angles = m_Store_Angles;
	m_Velocity.clear();
}


void CBasePhysics::goBackInTime()
{
	CBaseEntity::goBackInTime();	//odstrani border
	int i = 0;
	for( std::list<BackTimeData>::reverse_iterator riter = m_BackTimeData.rbegin(); riter != m_BackTimeData.rend(); ++riter )
	{
		//jakmile nalezneme border, tak koncime pruchod
		if( (*riter).type == EVENT_POSITION )
		{
			teleport( Vector( (*riter).values.position ) - origin, false );
			i++;
			if( i == 2 )
				return;
		}
		else if( (*riter).type == EVENT_ANGLES )
		{
			angles = Point ( (*riter).values.position );
			i++;
			if( i == 2 )
				return;
		}
	}
}

#include "game.h"

void CBasePhysics::updateBackTime()
{
	//store position
	m_BackTimeData.push_front( BackTimeData() );
	m_BackTimeData.front().event_time = game.m_uiGameTime;
	m_BackTimeData.front().type = EVENT_POSITION;
	*((Vector*)(m_BackTimeData.front().values.position)) = origin;

	m_BackTimeData.push_front( BackTimeData() );
	m_BackTimeData.front().event_time = game.m_uiGameTime;
	m_BackTimeData.front().type = EVENT_ANGLES;
	*((Vector*)(m_BackTimeData.front().values.position)) = angles;
}

void CBasePhysics::compile()
{
	CBaseEntity::compile();
	createModel( m_iHullType );
	m_InterpolatedPosition.reset( origin );	//nastavime aktualni origin za startovni
	m_Store_Position = origin;
	m_Store_Angles = angles;
}


void CBasePhysics::acceptVector( const alg::Vector &v )
{
	teleport( v );
}

void CBasePhysics::teleport( const alg::Vector &v, bool interpolation )
{
	m_Model.translateStatic( v );
	origin = m_Model.m_topCenterPoint;
	if( !interpolation )
		m_InterpolatedPosition.reset( origin );
}

inline void CBasePhysics::render( RenderData &renderData )
{
	m_InterpolatedPosition.drawRun( renderData.frameTimes.period_percentage );
}

void CBasePhysics::update( float seconds )
{

	CBaseEntity::update( seconds );
	//removeOldBackEvents();
	m_InterpolatedPosition.update( origin );

	updateForces();
}

void CBasePhysics::updateForces()
{
	//musime pridat pusobeni gravitace :)))
	addForce( Vector( 0.0f, 0.0f, -gamevarsLibrary::getfData( g_Gravity ) * m_Weight ) );

	if( !m_bIsInAir )
		//a taky koeficient treni
		addFriction( gamevarsLibrary::getfData( g_FrictionNormalGround ) );
}


void CBasePhysics::addFriction( float friction )
{
	m_TotalFriction = (1.0f - (( 1.0f - friction ) * ( 1.0f - m_TotalFriction )));
}


void CBasePhysics::addForce( const Force &force )
{
	m_TotalForce += force;
}

#include "sys_controller.h"

void CBasePhysics::prepareForCollision()
{
	//musime vypocitat rychlost ze sily
	//musime prevese rychlost v m/s na rychlost v (bodech za 30ms)	1m = 64bodu

	//m_TotalForce -= m_TotalForce * m_TotalFriction;

	m_Velocity += (m_TotalForce / m_Weight) * systemController::RUNTIME_PERIOD_S;	//rychlost v m/s
	if( !m_bIsInAir )
		m_Velocity -= m_Velocity * m_TotalFriction;

	m_TotalForce.clear();	//reset rychlosti
	m_TotalFriction = 0.0f;

	//zde budeme mozna muset provest eulerovu metodu

	m_Model.m_vecMovement = m_Velocity * systemController::RUNTIME_PERIOD_S * POINTS_PER_METER;
}

void CBasePhysics::setMoveVector( const alg::Vector &v)
{ 
	m_Model.m_vecMovement = v;

	m_Velocity = m_Model.m_vecMovement / POINTS_PER_METER / systemController::RUNTIME_PERIOD_S;
}

Force CBasePhysics::getForceOfMovement()
{
	return m_Velocity / systemController::RUNTIME_PERIOD_S * m_Weight;
}
