/*********************************************************************//**
*	BaseEntity
*	Abstraktni trida definuji spolecne vlastnosti vsech entit. je jejich
*	spolecnym zakladem.
*	
*	author: Michal Jirous
*	date: 10.03.2009
*	file: ent_baseentity.cpp
**********************************************************************/

#include "ent_baseentity.h"

typedef std::map<std::string, entityCreatorFunc> entityMap_t;

std::string getParameterValue( parameters_t &parametersMap, std::string key )
{
	parameters_t::iterator iter = parametersMap.find( key );
	if( iter != parametersMap.end() )
		return iter->second;
	return "";
}

entityMap_t *g_pEntityMap;
bool init = false;

EntityMaker::EntityMaker( std::string name, entityCreatorFunc func )
{
	if( !init )
	{
		g_pEntityMap = new std::map<std::string, entityCreatorFunc>();
		init = true;
	}


	g_pEntityMap->insert( make_pair( name, func ) );
}

CBaseEntity* getNewEntity( std::string name )
{
	entityMap_t::iterator iter = g_pEntityMap->find( name );
	if( iter != g_pEntityMap->end() )
		return iter->second();
	return NULL;
}

void CBaseEntity::insertBorder()
{
	m_BackTimeData.push_front( BackTimeData() );
	m_BackTimeData.front().event_time = 0;
	m_BackTimeData.front().type = EVENT_BORDER;
}


CBaseEntity::CBaseEntity()
{
	m_iProperties = 0;
	m_SolidModel = -1;
	m_pNearestFace = NULL;
	m_bIsBlending = false;
	m_pSortBoundingBox = NULL;
	m_TargetList = NULL;
	insertBorder();
	m_iMaterial = MATERIAL_FACE_BASED;	//default
	rendermode = RENDER_MODE_NORMAL;
	renderamt = 0.0f;
}

#include "level_loader.h"

void CBaseEntity::setParameters( parameters_t &parametersMap )
{
	std::string model = getParameterValue( parametersMap, "model" );
	if( !model.empty() )
	{
		model = model.substr( 1 );
		int modelId = atoi( model.c_str() );
		if( modelId != 0 )
		{
			setSolidModel( modelId );

			origin = levelLoader.m_LevelData.models[modelId].bounds.getCenterPoint();

		}
	}

	std::string value = getParameterValue( parametersMap, "origin" );
	if( !value.empty() )
		origin = Vector( value );

	value = getParameterValue( parametersMap, "angles" );
	if( !value.empty() )
	{
		angles = Vector( value );		
		
		angles = Vector( -angles[0], 0, angles[1] );
	}



	value = getParameterValue( parametersMap, "rendermode" );
	if( !value.empty() )
	{
		rendermode = atoi( value.c_str() );
		if( rendermode != RENDER_MODE_NORMAL )	//blending
		{
			m_iProperties |= ENT_PARM_BLENDING_CARE;
			m_bIsBlending = true;
		}
	}

	value = getParameterValue( parametersMap, "rendercolor" );
	if( !value.empty() )
	{
		rendercolor = Color(value);
		rendercolor.x /= 255.0f ;
		rendercolor.y /= 255.0f ;
		rendercolor.z /= 255.0f ;
	}

	value = getParameterValue( parametersMap, "renderamt" );
	if( !value.empty() )
	{
		int v = atoi(value.c_str());
		if( v == 255 )
			m_bIsBlending = false;
		renderamt =  (float)( v ) / 255.0f ;
	}

	m_sName = getParameterValue( parametersMap, "targetname" );
	m_sTarget = getParameterValue( parametersMap, "target" );

	value = getParameterValue( parametersMap, "spawnflags" );
	if( !value.empty() )
	{
		passFlags( atoi(value.c_str() ) );
	}
	
}
#include "level_render.h"
void CBaseEntity::setRenderMode()
{
	glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT );
	if( rendermode == 4 )
	{
		glColor4f(rendercolor.x, rendercolor.y, rendercolor.z, renderamt );
		
		
		if( renderamt < 1.0f )
		{
			glEnable( GL_BLEND );
			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
			
		}
		else
			glEnable( GL_ALPHA_TEST );
		renderer.setAlphaRendering( true );
	}


}

void CBaseEntity::unsetRenderMode()
{
	glPopAttrib();
	if( rendermode == 4 )
	{
		renderer.setAlphaRendering( false );
	}
}



//inline std::string CBaseEntity::getClassName()
//{
//	return m_sClassName;
//}

#include "game.h"

void CBaseEntity::removeOldBackEvents()
{
	//musime projit seznam udalosti a jestli uz je neco moc stary, tak to smazeme
	for( std::list<BackTimeData>::iterator iter = m_BackTimeData.begin(); iter != m_BackTimeData.end();  )
	{
		//jakmile nalezneme border, tak koncime pruchod
		if( (*iter).type == EVENT_BORDER )
			return;

		if( (*iter).event_time + BACK_TIME_MS_REAL < game.getGameTime() )
		{
			iter = m_BackTimeData.erase( iter );
		}
		else
			++iter;
	}
}

BackTimeData *CBaseEntity::getLastStoredEvent()
{
	if( !m_BackTimeData.empty() && m_BackTimeData.back().type != EVENT_BORDER )
		return &m_BackTimeData.back();
	return NULL;
}

void CBaseEntity::deleteLastStoredEvent()
{
	if( !m_BackTimeData.empty() && m_BackTimeData.back().type != EVENT_BORDER )
		m_BackTimeData.pop_back();
}












inline size_t CBaseEntity::getSolidModel()
{
	return m_SolidModel;
}












#include "level_collision.h"
#include "level_loader.h"

bool CBaseEntity::basicSolidCollisionDetection(alg::CollisionData &collData)
{
	if( m_SolidModel != 0 )
		return collisionSystem.BSPTreeCollision( collData, levelLoader.m_LevelData.nodes[ levelLoader.m_LevelData.models[ m_SolidModel ].headnode[HULL_REALWORLD] ] );
	return false;
}

bool CBaseEntity::collisionDetection( CollisionData &collData )
{
	return basicSolidCollisionDetection( collData );
}


bool CBaseEntity::basicSolidRayTracing( RayTraceData &rayData )
{
	if( m_SolidModel != 0 )
		return collisionSystem.rayTrace( rayData, levelLoader.m_LevelData.models[ m_SolidModel ].headnode[HULL_REALWORLD] );
	return false;
}

bool CBaseEntity::rayTrace( RayTraceData &rayData )
{
	return basicSolidRayTracing( rayData );
}


void CBaseEntity::restart()
{
	m_BackTimeData.clear();	//no future data
	insertBorder();	//border tam musi byt vzdy
}

#include "level_render.h"



void CBaseEntity::compile()
{
	m_TargetList = levelLoader.m_Entities.getEntitiesByName( m_sTarget );
}





void CBaseEntity::update( float seconds )
{
	removeOldBackEvents();
	BackTimeData* data;
	while( (data = getLastStoredEvent()) )
	{
		if( data->type == EVENT_BORDER )
			break;

		if( data->event_time < game.getGameTime() )
		{
			passStoredEvent( *data );
			deleteLastStoredEvent();
			continue;
		}
		break;
	}
		
}


void CBaseEntity::goBackInTime()
{	
	//jde pouze o to odstranit nejposlednejsi barieru
	if( m_BackTimeData.empty() )
		return;
	std::list<BackTimeData>::iterator iter = m_BackTimeData.end();
	do
	{
		--iter;
		if( (*iter).type == EVENT_BORDER )
		{	
			m_BackTimeData.erase( iter );
			break;
		}
	}
	while( iter != m_BackTimeData.begin() );

	insertBorder();	//ale samozrejme musim vlozit novy border na zacatek

}

