/* Copyright (C) 2009 Wildfire Games.
 * This file is part of 0 A.D.
 *
 * 0 A.D. is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * 0 A.D. is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "precompiled.h"

#include "graphics/GameView.h"
#include "graphics/Model.h"
#include "graphics/Sprite.h"
#include "graphics/Terrain.h"
#include "graphics/Unit.h"
#include "graphics/UnitManager.h"
#include "maths/MathUtil.h"
#include "maths/scripting/JSInterface_Vector3D.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "renderer/Renderer.h"
#include "renderer/WaterManager.h"
#include "scripting/ScriptableComplex.inl"

#include "Aura.h"
#include "Collision.h"
#include "Entity.h"
#include "EntityFormation.h"
#include "EntityManager.h"
#include "EntityTemplate.h"
#include "EntityTemplateCollection.h"
#include "EventHandlers.h"
#include "Formation.h"
#include "FormationManager.h"
#include "PathfindEngine.h"
#include "ProductionQueue.h"
#include "Simulation.h"
#include "Stance.h"
#include "TechnologyCollection.h"
#include "TerritoryManager.h"

#include <algorithm>

#include "ps/GameSetup/Config.h"

const float MAX_ROTATION_RATE = 2*(float)M_PI; // radians per second

CEntity::CEntity( CEntityTemplate* base, CVector3D position, float orientation, const std::set<CStr8>& actorSelections, const CStrW* building )
{
	ent_flags = 0;

	m_position = position;
	m_orientation.X = 0;
	m_orientation.Y = orientation;
	m_orientation.Z = 0;
	m_ahead.x = sin( m_orientation.Y );
	m_ahead.y = cos( m_orientation.Y );
	m_position_previous = m_position;
	m_orientation_smoothed = m_orientation_previous = m_orientation;
	m_player = NULL;

	m_productionQueue = new CProductionQueue( this );
	m_stance = new CHoldStance( this );

	for( int t = 0; t < EVENT_LAST; t++ )
	{
		AddProperty( EventNames[t], &m_EventHandlers[t], false );
		AddHandler( t, &m_EventHandlers[t] );
	}

	m_collisionPatch = NULL;

	// Set our parent unit and build us an actor.
	m_actor = NULL;
	m_bounds = NULL;

	m_lastState = -1;
	entf_set(ENTF_TRANSITION);
	m_fsm_cyclepos = NOT_IN_CYCLE;

	m_base = base;

	m_actorSelections = actorSelections;
	LoadBase();

	if( m_bounds )
	m_bounds->SetPosition( m_position.X, m_position.Z );

	m_graphics_position = m_position;
	m_graphics_orientation = m_orientation;
	m_actor_transform_valid = false;
	entf_clear(ENTF_HAS_RALLY_POINT);

	entf_clear(ENTF_DESTROYED);

	m_selected = false;
	entf_clear(ENTF_IS_RUNNING);
	entf_clear(ENTF_SHOULD_RUN);
	entf_clear(ENTF_TRIGGER_RUN);

	entf_clear(ENTF_HEALTH_DECAY);

	m_frameCheck = 0;
	m_lastCombatTime = -100;
	m_lastRunTime = -100;
	m_currentNotification = 0;
	m_currentRequest = 0;
	entf_set(ENTF_DESTROY_NOTIFIERS);

	m_formationSlot = (size_t)-1;
	m_formation = -1;
	m_grouped = -1;

	if( building )
		m_building = *building;

	m_extant = true;
	m_visible = true;

	m_rallyPoint = m_position;

	m_associatedTerritory = NULL;
	
	m_player = g_Game->GetPlayer( 0 );
}

CEntity::~CEntity()
{
	if( m_actor )
	{
		g_Game->GetWorld()->GetUnitManager().RemoveUnit( m_actor );
		delete( m_actor );
	}

	if( m_bounds )
	{
		delete( m_bounds );
	}

	delete m_productionQueue;

	delete m_stance;

	for( AuraTable::iterator it = m_auras.begin(); it != m_auras.end(); it++ )
	{
		delete it->second;
	}
	m_auras.clear();

	entf_set(ENTF_DESTROY_NOTIFIERS);
	for ( size_t i=0; i<m_listeners.size(); i++ )
		m_listeners[i].m_sender->DestroyNotifier( this );
	m_listeners.clear();
	DestroyAllNotifiers();

	CEntity* remove = this;
	g_FormationManager.RemoveUnit(remove);
}

void CEntity::LoadBase()
{
	size_t previous_unit_id = invalidUnitId;

	if( m_actor )
	{
		previous_unit_id = m_actor->GetID();
		g_Game->GetWorld()->GetUnitManager().RemoveUnit( m_actor );
		delete( m_actor );
		m_actor = NULL;
	}
	if( m_bounds )
	{
		delete( m_bounds );
		m_bounds = NULL;
	}

	CStr actorName ( m_base->m_actorName ); // convert CStrW->CStr8

	m_actor = g_Game->GetWorld()->GetUnitManager().CreateUnit( actorName, this, m_actorSelections );
	if( m_actor )
		m_actor->SetID( previous_unit_id );

	// Set up our instance data

	SetBase( m_base );
	m_classes.SetParent( &( m_base->m_classes ) );
	SetNextObject( m_base );

	if( m_base->m_bound_type == CBoundingObject::BOUND_CIRCLE )
	{
		m_bounds = new CBoundingCircle( m_position.X, m_position.Z, m_base->m_bound_circle );
	}
	else if( m_base->m_bound_type == CBoundingObject::BOUND_OABB )
	{
		m_bounds = new CBoundingBox( m_position.X, m_position.Z, m_ahead, m_base->m_bound_box );
	}

	m_actor_transform_valid = false;

	if( m_player )
	{
		// Make sure the actor has the right player colour
		m_actor->SetPlayerID( m_player->GetPlayerID() );
	}

	// Re-enter all our auras so they can take into account our new traits
	ExitAuras();

	// Resize sectors array	
	m_sectorValues.resize(m_base->m_sectorDivs);
	for ( int i=0; i<m_base->m_sectorDivs; ++i )
		m_sectorValues[i] = false;
}

void CEntity::initAuraData()
{
	if ( m_auras.empty() )
		return;
	m_unsnappedPoints.resize(m_auras.size());
	size_t i=0;
	for ( AuraTable::iterator it=m_auras.begin(); it!=m_auras.end(); ++it, ++i )
	{
		m_unsnappedPoints[i].resize(AURA_CIRCLE_POINTS);
		float radius = it->second->m_radius;

		for ( int j=0; j<AURA_CIRCLE_POINTS; ++j )
		{
			float val = j * 2*(float)M_PI / AURA_CIRCLE_POINTS;
			m_unsnappedPoints[i][j] = CVector2D( cosf(val)*radius, 
											sinf(val)*radius );
		}
	}
}

void CEntity::removeObstacle()
{
#ifdef USE_DCDT
	if(g_Pathfinder.dcdtInitialized)
	{
		g_Pathfinder.dcdtPathfinder.remove_polygon(m_dcdtId);
		g_Pathfinder.dcdtPathfinder.DeleteAbstraction();
		g_Pathfinder.dcdtPathfinder.Abstract();

		if(g_ShowPathfindingOverlay)
		{
			g_Pathfinder.drawTriangulation();
		}
	}
#endif // USE_DCDT
}

void CEntity::Kill(bool keepActor)
{
	if( entf_get( ENTF_DESTROYED ) )
	{
		return;		// We were already killed this frame
	}
	entf_set(ENTF_DESTROYED);

	CEventDeath evt;
	DispatchEvent( &evt );

	g_FormationManager.RemoveUnit(this);
	
	entf_set(ENTF_DESTROY_NOTIFIERS);
	for ( size_t i=0; i<m_listeners.size(); i++ )
		m_listeners[i].m_sender->DestroyNotifier( this );
	m_listeners.clear();
	DestroyAllNotifiers();

	for( AuraTable::iterator it = m_auras.begin(); it != m_auras.end(); it++ )
	{
		it->second->RemoveAll();
		delete it->second;
	}
	m_auras.clear();

	ExitAuras();

	ClearOrders();

	SAFE_DELETE(m_bounds);

	m_extant = false;

	UpdateCollisionPatch();
	
	//Kai: added to remove the entity in the polygon soup (for triangulation)
	removeObstacle();
	

//	g_Selection.RemoveAll( me );

//	g_EntityManager.m_refd[me.m_handle] = false; // refd must be made false when DESTROYED is set
//	g_EntityManager.SetDeath(true); // remember that a unit died this frame
//
//	g_EntityManager.RemoveUnitCount(this);	//Decrease population

	// If we have a death animation and want to keep the actor, play that animation
	if( keepActor && m_actor && 
		m_actor->HasAnimation( "death" ) )
	{
		// Prevent "wiggling" as we try to interpolate between here and our death position (if we were moving)
		m_graphics_position = m_position;
		m_position_previous = m_position;
		m_graphics_orientation = m_orientation;
		m_orientation_smoothed = m_orientation;
		m_orientation_previous = m_orientation;

		SnapToGround();
		
		// Conform to the ground
		CVector2D targetXZ = g_Game->GetWorld()->GetTerrain()->GetSlopeAngleFace(this);
		m_orientation.X = clamp( targetXZ.x, -1.0f, 1.0f );
		m_orientation.Z = clamp( targetXZ.y, -1.0f, 1.0f );
		m_orientation_unclamped.x = targetXZ.x;
		m_orientation_unclamped.y = targetXZ.y;

		UpdateActorTransforms();
		m_actor_transform_valid = true;

		// Play death animation and keep the actor in the game in a dead state 
		// (TODO: remove the actor after some time through some kind of fading mechanism)
		m_actor->SetAnimationState( "death", true, 1.f, 0.f, false, L"" );
	}
	else
	{
		g_Game->GetWorld()->GetUnitManager().DeleteUnit( m_actor );
		m_actor = NULL;

		me = HEntity(); // Will deallocate the entity, assuming nobody else has a reference to it
	}
}

void CEntity::SetPlayer(CPlayer *pPlayer)
{
	m_player = pPlayer;

	// This should usually be called CUnit::SetPlayerID, so we don't need to
	// update the actor here.

	// If we're a territory centre, change the territory's owner
	if( m_associatedTerritory )
		m_associatedTerritory->owner = pPlayer;
}

void CEntity::UpdateActorTransforms()
{
	CMatrix3D m;
	CMatrix3D mXZ;
	float Cos = cosf( m_graphics_orientation.Y );
	float Sin = sinf( m_graphics_orientation.Y );

	m._11=-Cos;  m._12=0.0f; m._13=-Sin;  m._14=0.0f;
	m._21=0.0f; m._22=1.0f; m._23=0.0f; m._24=0.0f;
	m._31=Sin; m._32=0.0f; m._33=-Cos;  m._34=0.0f;
	m._41=0.0f; m._42=0.0f; m._43=0.0f; m._44=1.0f;

	mXZ.SetXRotation( m_graphics_orientation.X );
	mXZ.RotateZ( m_graphics_orientation.Z );
	mXZ = m*mXZ;
	mXZ.Translate(m_graphics_position);

	if( m_actor )
		m_actor->GetModel().SetTransform( mXZ );
}

void CEntity::SnapToGround()
{
	m_graphics_position.Y = GetAnchorLevel( m_graphics_position.X, m_graphics_position.Z );
}

void CEntity::UpdateXZOrientation()
{
	// Make sure m_ahead is correct
	m_ahead.x = sin( m_orientation.Y );
	m_ahead.y = cos( m_orientation.Y );

	CVector2D targetXZ = g_Game->GetWorld()->GetTerrain()->GetSlopeAngleFace(this);

	if ( !m_base ) 
	{
		return;
	}

	m_orientation.X = clamp( targetXZ.x, -m_base->m_anchorConformX, m_base->m_anchorConformX );
	m_orientation.Z = clamp( targetXZ.y, -m_base->m_anchorConformZ, m_base->m_anchorConformZ );
	m_orientation_unclamped.x = targetXZ.x;
	m_orientation_unclamped.y = targetXZ.y;
}

jsval CEntity::GetClassSet()
{
	CStrW result = m_classes.GetMemberList(); 
	return( ToJSVal( result ) );
}

void CEntity::SetClassSet( jsval value )
{
	CStr memberCmdList = ToPrimitive<CStrW>( value );
	m_classes.SetFromMemberList(memberCmdList);

	RebuildClassSet();
}

void CEntity::RebuildClassSet()
{
	m_classes.Rebuild();
	InheritorsList::iterator it;
	for( it = m_Inheritors.begin(); it != m_Inheritors.end(); it++ )
		(*it)->RebuildClassSet();
}

void CEntity::Update( int timestep )
{
	if( !m_extant ) return;

	m_position_previous = m_position;
	m_orientation_previous = m_orientation_smoothed;

	CalculateRegen( timestep );

	if ( entf_get(ENTF_TRIGGER_RUN) )
		m_frameCheck++;

	if ( m_frameCheck != 0 )
	{
		entf_set(ENTF_SHOULD_RUN);
		entf_clear(ENTF_TRIGGER_RUN);
		m_frameCheck = 0;
	}

	m_productionQueue->Update( timestep );

	// Note: aura processing is done before state processing because the state
	// processing code is filled with all kinds of returns

	PROFILE_START( "aura processing" );

	for( AuraTable::iterator it = m_auras.begin(); it != m_auras.end(); it++ )
	{
		it->second->Update( timestep );
	}

	PROFILE_END( "aura processing" );

	UpdateOrders( timestep );


	// Calculate smoothed rotation: rotate around Y by at most MAX_ROTATION_RATE per second

	float delta = m_orientation.Y - m_orientation_smoothed.Y;
	// Wrap delta to -M_PI..M_PI
	delta = fmod(delta + (float)M_PI, 2*(float)M_PI); // range -2PI..2PI
	if (delta < 0) delta += 2*(float)M_PI; // range 0..2PI
	delta -= (float)M_PI; // range -M_PI..M_PI
	// Clamp to max rate
	float deltaClamped = clamp(delta, -MAX_ROTATION_RATE*timestep/1000.f, +MAX_ROTATION_RATE*timestep/1000.f);
	// Calculate new orientation, in a peculiar way in order to make sure the
	// result gets close to m_orientation (rather than being n*2*M_PI out)
	float newY = m_orientation.Y + deltaClamped - delta;
	// Apply the smoothed rotation
	m_orientation_smoothed = CVector3D(
		m_orientation.X,
		newY,
		m_orientation.Z
	);
}

void CEntity::UpdateOrders( int timestep )
{
	// The process[...] functions return 'true' if the order at the top of the stack
	// still needs to be (re-)evaluated; else 'false' to terminate the processing of
	// this entity in this timestep.

	PROFILE_START( "state processing" );

	if( entf_get(ENTF_IS_RUNNING) )
	{
		m_lastRunTime = g_Game->GetSimulation()->GetTime();
	}

	if( m_orderQueue.empty() )
	{
		// We are idle. Tell our stance in case it wants us to do something.
		PROFILE( "unit ai" );
		m_stance->OnIdle();
	}

	while( !m_orderQueue.empty() )
	{
		CEntityOrder* current = &m_orderQueue.front();
		CStr name = me;
#ifdef DEBUG_SYNCHRONIZATION
		debug_printf(L"Order for %ls: %d (src %d)\n", 
			m_base->m_Tag.c_str(), current->m_type, current->m_source);
#endif

		if( current->m_type != m_lastState )
		{
			entf_set(ENTF_TRANSITION);
			m_fsm_cyclepos = NOT_IN_CYCLE;

			PROFILE( "state transition / order" );

			CEntity* target = NULL;
			if( current->m_target_entity )
				target = &( *( current->m_target_entity ) );

			CVector3D worldPosition = (CVector3D)current->m_target_location;

			CEventOrderTransition evt( m_lastState, current->m_type, target, worldPosition );

			if( !DispatchEvent( &evt ) )
			{
				m_orderQueue.pop_front();
				continue;
			}
			else if( target )
			{
				current->m_target_location = worldPosition;
				current->m_target_entity = target->me;
			}

			m_lastState = current->m_type;
		}
		else
		{
			entf_clear(ENTF_TRANSITION);
		}

		switch( current->m_type )
		{
			case CEntityOrder::ORDER_GOTO_NOPATHING:
			case CEntityOrder::ORDER_GOTO_COLLISION:
			case CEntityOrder::ORDER_GOTO_SMOOTHED:
				if( ProcessGotoNoPathing( current, timestep ) )
					break;
				UpdateCollisionPatch();
				return;
			case CEntityOrder::ORDER_GOTO_NOPATHING_CONTACT:
				if( ProcessGotoNoPathingContact( current, timestep ) )
					break;
				UpdateCollisionPatch();
				return;
			case CEntityOrder::ORDER_CONTACT_ACTION:
				// Choose to run if and only if order.m_run is set
				entf_set_to(ENTF_TRIGGER_RUN, current->m_run);
				if( !entf_get(ENTF_TRIGGER_RUN) )
					entf_set_to(ENTF_SHOULD_RUN, false);
				if( ProcessContactAction( current, timestep, true ) )
					break;
				UpdateCollisionPatch();
				return;
			case CEntityOrder::ORDER_START_CONSTRUCTION:
				{
					CEventStartConstruction evt( current->m_new_obj );
					m_orderQueue.pop_front();
					DispatchEvent( &evt );
				}
				break;
			case CEntityOrder::ORDER_PRODUCE:
				ProcessProduce( current );
				m_orderQueue.pop_front();
				break;
			case CEntityOrder::ORDER_CONTACT_ACTION_NOPATHING:
				if( ProcessContactActionNoPathing( current, timestep ) )
					break;
				UpdateCollisionPatch();
				return;
			case CEntityOrder::ORDER_GOTO_WAYPOINT:
				if ( ProcessGotoWaypoint( current, timestep, false ) )
					break;
				UpdateCollisionPatch();
				return;
			case CEntityOrder::ORDER_GOTO_WAYPOINT_CONTACT:
				if ( ProcessGotoWaypoint( current, timestep, true ) )
					break;
				UpdateCollisionPatch();
				return;
			case CEntityOrder::ORDER_GOTO:
			case CEntityOrder::ORDER_RUN:
				// Choose to run if and only if type == ORDER_RUN
				entf_set_to(ENTF_TRIGGER_RUN, current->m_type == CEntityOrder::ORDER_RUN);
				if( !entf_get(ENTF_TRIGGER_RUN) )
					entf_set_to(ENTF_SHOULD_RUN, false);
				if( ProcessGoto( current, timestep ) )
					break;
				UpdateCollisionPatch();
				return;
			case CEntityOrder::ORDER_PATROL:
				if( ProcessPatrol( current, timestep ) )
					break;
				UpdateCollisionPatch();
				return;
			case CEntityOrder::ORDER_PATH_END_MARKER:
				m_orderQueue.pop_front();
				break;
			default:
				debug_warn( L"Invalid entity order" );
		}
	}

	if( m_orderQueue.empty() ) 
	{
		// If we have no orders, stop running
		entf_clear(ENTF_IS_RUNNING);
		entf_clear(ENTF_SHOULD_RUN);
	}

	PROFILE_END( "state processing" );

	// If we get to here, it means we're idle or dead (no orders); update the animation

	if( m_actor )
	{
		PROFILE( "animation updates" );
		if( m_extant )
		{
			if( ( m_lastState != -1 ) || !m_actor->GetModel().GetAnimation() )
			{
				m_actor->SetAnimationState( "idle", false, 1.f, 0.f, false, L"" );
			}
		}
	}

	if( m_lastState != -1 )
	{
		PROFILE( "state transition event" );
		CVector3D vec(0, 0, 0);
		CEventOrderTransition evt( m_lastState, -1, 0, vec );
		DispatchEvent( &evt );

		m_lastState = -1;
	}
}

void CEntity::UpdateCollisionPatch()
{

}

#if AURA_TEST
void CEntity::UpdateAuras( int timestep_millis )
{
	std::vector<SAura>::iterator it_a;
	for( it_a = m_Auras.begin(); it_a != m_Auras.end(); it_a++ )
	{
		SAuraData& d = it_a->m_Data;
		std::set<CEntity*>
		& inRange = GetEntitiesWithinRange( m_position, d.m_Radius );

		std::vector<CEntity*>::iterator it1 = inRange.begin();
		std::vector<SAuraInstance>::iterator it2 = it_a->m_Influenced.begin();

		while( WORLD_IS_ROUND )
		{
			if( it1 == inRange.end() )
			{
				// No more in range => anything else in the influenced set must have gone
				// out of range.
				for( ; it2 != it_a->m_Influenced.end(); it2++ )
					UpdateAuras_LeaveRange( *it_a, it2->GetEntity() );

				break;
			}
			if( it2 == it_a->m_Influenced.end() )
			{
				// Everything else in the in-range set has only just come into range
				for( ; it1 != inRange.end(); it1++ )
					UpdateAuras_EnterRange( *it_a, *it );
				break;
			}
			CEntity* e1 = *it1, e2 = it2->GetEntity();
			if( e1 < e2 )
			{
				// A new entity e1 has just come into range.
				// Check to see if it can be affected by the aura.
				UpdateAuras_EnterRange( *it_a, e1 );
				++it1;
			}
			else if( e1 == e2 )
			{
				// The entity e1/e2 was previously in range, and still is.
				UpdateAuras_Normal( *it_a, e1 );
				++it1;
				++it2;
			}
			else
			{
				// The entity e2 was previously in range, but is no longer.
				UpdateAuras_LeaveRange( *it_a, e2 );
				++it2;
			}
		}
	}
}

void UpdateAuras_EnterRange( SAura& aura, CEntity* e )
{
	if( aura.m_Recharge )
		return( false );
	// Check to see if the entity is eligable
	if( !UpdateAuras_IsEligable( aura.m_Data, e ) )
		return; // Do nothing.

	SAuraInstance ai;
	ai.m_Influenced = e;
	ai.m_EnteredRange = ai.m_LastInRange = 0;
	ai.m_Applied = -1;

	// If there's no timer, apply the effect now.
	if( aura.m_Data.m_Time == 0 )
	{
		e->ApplyAuraEffect( aura.m_Data );
		ai.m_Applied = 0;
		aura.m_Recharge = aura.m_Data.m_Cooldown;
	}

	aura.m_Influenced.push_back( ai );
}

void UpdateAuras_Normal( SAura& aura, CEntity* e )
{
	// Is the entity no longer eligable?
	if( !UpdateAuras_IsEligable( aura.m_Data, e ) )
	{
		//}
	}

	bool UpdateAuras_IsEligable( SAuraData& aura, CEntity* e )
	{
		if( e == this )
		{
			if( !( aura.m_Allegiance & SAuraData::SELF ) )
				return( false );
		}
		else if( e->m_player == GetGaiaPlayer() )
		{
			if( !( aura.m_Allegiance & SAuraData::GAIA ) )
				return( false );
		}
		else if( e->m_player == m_player )
		{
			if( !( aura.m_Allegiance & SAuraData::PLAYER ) )
				return( false );
		}
		// TODO: Allied players
		else
		{
			if( !( aura.m_Allegiance & SAuraData::ENEMY ) )
				return( false );
		}
		if( e->m_hp > e->m_hp_max * aura.m_Hitpoints )
			return( false );
		return( true );
	}
}
#endif

bool CEntity::Initialize()
{
	return true;
}

/*
void CEntity::Tick()
{
	CEventTick evt;
	DispatchEvent( &evt );
}
*/

void CEntity::ClearOrders()
{
	if ( m_orderQueue.empty() )
		return;
	CIdleEvent evt( m_orderQueue.front(), m_currentNotification );
	DispatchEvent(&evt);
	m_orderQueue.clear();
}
void CEntity::PopOrder()
{
	if ( m_orderQueue.empty() )
		return;
	CIdleEvent evt( m_orderQueue.front(), m_currentNotification );
	DispatchEvent(&evt);

	m_orderQueue.pop_front();
}
void CEntity::PushOrder( const CEntityOrder& order )
{
	CEventPrepareOrder evt( order.m_target_entity, order.m_type, order.m_action, order.m_name );
	if( DispatchEvent(&evt) )
	{
		if (order.m_type == CEntityOrder::ORDER_SET_RALLY_POINT)
		{
			// It doesn't make sense to queue this type of order; just set the rally point
			entf_set(ENTF_HAS_RALLY_POINT);
			m_rallyPoint = order.m_target_location;
		}
		else if (order.m_type == CEntityOrder::ORDER_SET_STANCE)
		{
			// It doesn't make sense to queue this type of order; just set the stance
			m_stanceName = order.m_name;
			StanceChanged();
		}
		else
		{
			m_orderQueue.push_back( order );
			if(evt.m_notifyType != CEntityListener::NOTIFY_NONE)
			{
				CheckListeners( evt.m_notifyType, evt.m_notifySource );
			}
		}
	}
}

void CEntity::DispatchNotification( const CEntityOrder& order, int type )
{
	CEventNotification evt( order, type );
	DispatchEvent( &evt );
}

struct isListenerSender
{
	CEntity* sender;
	isListenerSender(CEntity* sender) : sender(sender) {}
	bool operator()(CEntityListener& listener)
	{
		return listener.m_sender == sender;
	}
};

int CEntity::DestroyNotifier( CEntity* target )
{
	//Stop listening
	// (Don't just loop and use 'erase', because modifying the deque while
	// looping over it is a bit dangerous)
	std::deque<CEntityListener>::iterator newEnd = std::remove_if(
		target->m_listeners.begin(), target->m_listeners.end(),
		isListenerSender(this));
	target->m_listeners.erase(newEnd, target->m_listeners.end());

	//Get rid of our copy
	std::vector<CEntity*>::iterator newEnd2 = std::remove_if(
		m_notifiers.begin(), m_notifiers.end(),
		bind2nd(std::equal_to<CEntity*>(), target));
	int removed = m_notifiers.end() - newEnd2;
	//m_notifiers.erase(newEnd2, m_notifiers.end());
	m_notifiers.resize(m_notifiers.size() - removed);
	return removed;
}
void CEntity::DestroyAllNotifiers()
{
	debug_assert(entf_get(ENTF_DESTROY_NOTIFIERS));
	//Make them stop listening to us
	while ( ! m_notifiers.empty() )
		DestroyNotifier( m_notifiers[m_notifiers.size()-1] );
}
CEntityFormation* CEntity::GetFormation()
{
	if ( m_formation < 0 )
		return NULL;
	return g_FormationManager.GetFormation(m_formation);
}
void CEntity::DispatchFormationEvent( int type )
{
	CFormationEvent evt( type );
	DispatchEvent( &evt );
}
void CEntity::Repath()
{
	debug_printf(L"Repath\n");
	CVector2D destination;
	CEntityOrder::EOrderSource orderSource = CEntityOrder::SOURCE_PLAYER;

	if( m_orderQueue.empty() )
		return;

	while( !m_orderQueue.empty() &&
			( ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_COLLISION )
			  || ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_NOPATHING )
			  || ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_SMOOTHED ) ) )
	{
		destination = m_orderQueue.front().m_target_location;
		orderSource = m_orderQueue.front().m_source;
		m_orderQueue.pop_front();
	}
	g_Pathfinder.RequestPath( me, destination, orderSource );
}

void CEntity::Reorient()
{
	m_graphics_orientation = m_orientation;
	m_orientation_previous = m_orientation;
	m_orientation_smoothed = m_orientation;

	m_ahead.x = sin( m_orientation.Y );
	m_ahead.y = cos( m_orientation.Y );
	if( m_bounds->m_type == CBoundingObject::BOUND_OABB )
		((CBoundingBox*)m_bounds)->SetOrientation( m_ahead );
	UpdateActorTransforms();
}

void CEntity::Teleport()
{
	m_position_previous = m_position;
	m_graphics_position = m_position;
	if (m_bounds)
		m_bounds->SetPosition( m_position.X, m_position.Z );
	UpdateActorTransforms();
	UpdateCollisionPatch();

	// TODO: Repath breaks things - entities get sent to (0,0) if they're moved in
	// Atlas. I can't see Teleport being used anywhere else important, so
	// hopefully it won't hurt to just remove it for now...
//	Repath();
}

void CEntity::StanceChanged()
{
	delete m_stance;
	m_stance = 0;

	if( m_stanceName == "aggress" )
	{
		m_stance = new CAggressStance( this );
	}
	else if( m_stanceName == "defend" )
	{
		m_stance = new CDefendStance( this );
	}
	else if( m_stanceName == "stand" )
	{
		m_stance = new CStandStance( this );
	}
	else	// m_stanceName == "hold" or undefined stance
	{
		m_stance = new CHoldStance( this );
	}
}

void CEntity::CheckSelection()
{
/*
	if( m_selected )
	{
		if( !g_Selection.IsSelected( me ) )
			g_Selection.AddSelection( me );
	}
	else
	{
		if( g_Selection.IsSelected( me ) )
			g_Selection.RemoveSelection( me );
	}
*/
}

void CEntity::CheckGroup()
{
/*
	g_Selection.ChangeGroup( me, -1 ); // Ungroup
	if( ( m_grouped >= 0 ) && ( m_grouped < MAX_GROUPS ) )
		g_Selection.ChangeGroup( me, m_grouped );
*/
}

void CEntity::Interpolate( float relativeoffset )
{
	CVector3D old_graphics_position = m_graphics_position;
	CVector3D old_graphics_orientation = m_graphics_orientation;

	relativeoffset = clamp( relativeoffset, 0.f, 1.f );

	m_graphics_position = ::Interpolate<CVector3D>( m_position_previous, m_position, relativeoffset );

	// Avoid wraparound glitches for interpolating angles.
	
	m_orientation.X = fmodf(m_orientation.X, 2*(float)M_PI); // (ensure the following loops can't take forever)
	m_orientation.Y = fmodf(m_orientation.Y, 2*(float)M_PI);
	m_orientation.Z = fmodf(m_orientation.Z, 2*(float)M_PI);

	while( m_orientation.Y < m_orientation_previous.Y - (float)M_PI )
		m_orientation_previous.Y -= 2 * (float)M_PI;
	while( m_orientation.Y > m_orientation_previous.Y + (float)M_PI )
		m_orientation_previous.Y += 2 * (float)M_PI;

	while( m_orientation.X < m_orientation_previous.X - (float)M_PI )
		m_orientation_previous.X -= 2 * (float)M_PI;
	while( m_orientation.X > m_orientation_previous.X + (float)M_PI )
		m_orientation_previous.X += 2 * (float)M_PI;

	while( m_orientation.Z < m_orientation_previous.Z - (float)M_PI )
		m_orientation_previous.Z -= 2 * (float)M_PI;
	while( m_orientation.Z > m_orientation_previous.Z + (float)M_PI )
		m_orientation_previous.Z += 2 * (float)M_PI;

	UpdateXZOrientation();

	m_graphics_orientation = ::Interpolate<CVector3D>( m_orientation_previous, m_orientation_smoothed, relativeoffset );

	// Mark the actor transform data as invalid if the entity has moved since
	// the last call to 'interpolate'.
	// position.Y is ignored because we can't determine the new value without
	// calling SnapToGround, which is slow. TODO: This may need to be adjusted to
	// handle flying units or moving terrain.
	if( m_graphics_orientation != old_graphics_orientation ||
		m_graphics_position.X != old_graphics_position.X ||
		m_graphics_position.Z != old_graphics_position.Z		
		)
	{															
		m_actor_transform_valid = false;
	}
	// Update the actor transform data when necessary.
	if( !m_actor_transform_valid )
	{
		SnapToGround();
		UpdateActorTransforms();
		m_actor_transform_valid = true;
	}
}

void CEntity::InvalidateActor()
{
	m_actor_transform_valid = false;
}

float CEntity::GetAnchorLevel( float x, float z )
{
	CTerrain *pTerrain = g_Game->GetWorld()->GetTerrain();
	float groundLevel = pTerrain->GetExactGroundLevel( x, z );
	if( m_base && m_base->m_anchorType==L"Ground" )
	{
		return groundLevel;
	}
	else
	{
		return std::max( groundLevel, g_Renderer.GetWaterManager()->m_WaterHeight );
	}
}

int CEntity::FindSector( int divs, float angle, float maxAngle, bool negative )
{
	float step=maxAngle/divs;
	if ( negative )
	{
		float tracker;
		int i=1, sectorRemainder;
		for ( tracker=-maxAngle/2.0f; tracker+step<0.0f; tracker+=step, ++i )
		{
			if ( angle > tracker && angle <= tracker+step ) 
				return i;
		}
		sectorRemainder = i;
		i=divs;
		for ( tracker=maxAngle/2.0f; tracker-step>0.0f; tracker-=step, --i )
		{
			if ( angle < tracker && angle >= tracker-step ) 
				return i;
		}
		return sectorRemainder;
	}
	else
	{
		int i=1;
		for (  float tracker=0.0f; tracker<maxAngle; tracker+=step, ++i )
		{
			if ( angle > tracker && angle <= tracker+step )
				return i;
		}
	}
	debug_warn(L"CEntity::FindSector() - invalid parameters passed.");
	return -1;
}

static inline float regen(float cur, float limit, float timestep, float regen_rate)
{
	if(regen_rate <= 0)
		return cur;
	return std::min(limit, cur + timestep / 1000.0f * regen_rate * limit );
}

static inline float decay(float cur, float limit, float timestep, float decay_rate)
{
	if(decay_rate <= 0)
		return cur;
	return std::max(0.0f, cur - timestep / 1000.0f * decay_rate * limit);
}

void CEntity::CalculateRegen(float timestep)
{
	// Health regen
	if(entf_get(ENTF_HEALTH_DECAY))
		m_healthCurr = decay(m_healthCurr, m_healthMax, timestep, m_healthDecayRate);
	else if(g_Game->GetSimulation()->GetTime() - m_lastCombatTime > m_healthRegenStart)
		m_healthCurr = regen(m_healthCurr, m_healthMax, timestep, m_healthRegenRate);
	
	// Stamina regen
	if( m_staminaMax > 0 )
	{
		if(entf_get(ENTF_IS_RUNNING))
			m_staminaCurr = decay(m_staminaCurr, m_staminaMax, timestep, m_runDecayRate);
		else if(m_orderQueue.empty())
			m_staminaCurr = regen(m_staminaCurr, m_staminaMax, timestep, m_runRegenRate);
	}
}

void CEntity::ExitAuras() 
{
	for( AuraSet::iterator it = m_aurasInfluencingMe.begin(); it != m_aurasInfluencingMe.end(); it++ )
	{
		(*it)->Remove( this );
	}
	m_aurasInfluencingMe.clear();
}
