// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

//
// Includes
//

#include <nel/misc/types_nl.h>

#include <cmath>
#include <map>

#include <nel/misc/types_nl.h>
#include <nel/misc/event_listener.h>
#include <nel/misc/command.h>
#include <nel/misc/log.h>
#include <nel/misc/time_nl.h>
#include <nel/misc/displayer.h>
#include <nel/misc/vector.h>
#include <nel/misc/vectord.h>
#include <nel/misc/time_nl.h>

#include <nel/3d/u_camera.h>
#include <nel/3d/u_driver.h>
#include <nel/3d/u_text_context.h>
#include <nel/3d/u_instance.h>
#include <nel/3d/u_scene.h>
#include <nel/3d/u_material.h>
#include <nel/3d/u_landscape.h>
#include <nel/3d/u_skeleton.h>

#include <nel/3d/u_visual_collision_manager.h>
#include <nel/3d/u_visual_collision_entity.h>

#include <nel/pacs/u_move_container.h>
#include <nel/pacs/u_move_primitive.h>
#include <nel/pacs/u_global_retriever.h>
#include <nel/pacs/u_global_position.h>

#include "snowballs_client.h"
#include "entities.h"
#include "pacs.h"
#include "animation.h"
#include "camera.h"
#ifdef NL_OS_WINDOWS
#include "sound.h"
#endif
#include "mouse_listener.h"
#include "landscape.h"

//
// Namespaces
//

using namespace std;
using namespace NLMISC;
using namespace NL3D;
using namespace NLPACS;

namespace SBCLIENT {

//
// Variables
//

// A map of entities. All entities are later referred by their unique id
map<uint32, CEntity>	Entities;

CEntity					*Self = NULL;

// The size of the world, in meter
float					WorldWidth = 20*160;
float					WorldHeight = 6*160;

// Entity Id, only used offline
uint32					NextEID = 1000000;

// The speed settings
float					PlayerSpeed = 10.0f;	// 6.5 km/h
float					SnowballSpeed = 15.0f;	// 36 km/h

// these variables are set with the config file

// Setup for the name up the character
float					EntityNameSize;
CRGBA					EntityNameColor;

bool _TestCLS = false;


// Set the state of the entity (Appear, Normal, Disappear)
void CEntity::setState (TState state)
{
	State = state;
	StateStartTime = CTime::getLocalTime ();
}


// Get an map iterator on a entity, specified by its id
EIT findEntity (uint32 eid, bool needAssert)
{
	EIT entity = Entities.find (eid);
	if (entity == Entities.end () && needAssert)
	{
		nlerror ("Entity %u not found", eid);
	}
	return entity;
}

// -- -- things like Creature, Effect, Scenery seem more flexible than Self, Other, Snowball
// -- -- random keywords: entitybehavior (animations), entityinteraction (targetable, menu, )
// Creates an entity, given its id, its type (Self, Other, Snowball), its start and server positions.
void addEntity (uint32 eid, std::string name, CEntity::TType type, const CVector &startPosition, const CVector &serverPosition)
{
//	nlinfo ("adding entity %u", eid);

	// Check that the entity doesn't exist yet
	EIT eit = findEntity (eid, false);
	if (eit != Entities.end ())
	{
		nlerror ("Entity %d already exist", eid);
	}

	// Create a new entity
	eit = (Entities.insert (make_pair (eid, CEntity()))).first;
	CEntity	&entity = (*eit).second;

	// Check that in the case the entity newly created is a Self, there's not a Self yet.
	if (type == CEntity::Self)
	{
		if (Self != NULL)
			nlerror("Self entity already created");

		Self = &entity;
	}

	// Set the entity up
	entity.Id = eid;
	entity.Type = type;
	entity.Name = name;
	entity.Position = startPosition;
	entity.Angle = 0.0f;
	entity.ServerPosition = serverPosition;
	entity.VisualCollisionEntity = VisualCollisionManager->createEntity();

	// setup the move primitive and the mesh instance depending on the type of entity
	switch (type)
	{
	case CEntity::Self:
		// create a move primitive associated to the entity
		entity.MovePrimitive = MoveContainer->addCollisionablePrimitive(0, 1);
		// it's a cylinder
		entity.MovePrimitive->setPrimitiveType(UMovePrimitive::_2DOrientedCylinder);
		// the entity should slide against obstacles
		entity.MovePrimitive->setReactionType(UMovePrimitive::Slide);
		// do not generate event if there is a collision
		entity.MovePrimitive->setTriggerType(UMovePrimitive::NotATrigger);
		// which entity should collide against me
		entity.MovePrimitive->setCollisionMask(OtherCollisionBit+SnowballCollisionBit+StaticCollisionBit);
		// the self collision bit
		entity.MovePrimitive->setOcclusionMask(SelfCollisionBit);
		// the self is an obstacle
		entity.MovePrimitive->setObstacle(true);
		// the size of the cylinder
		entity.MovePrimitive->setRadius(1.0f);
		entity.MovePrimitive->setHeight(1.8f);
		// only use one world image, so use insert in world image 0
		entity.MovePrimitive->insertInWorldImage(0);
		// retrieve the start position of the entity
		entity.MovePrimitive->setGlobalPosition(CVectorD(startPosition.x, startPosition.y, startPosition.z), 0);

		// create instance of the mesh character
		entity.Instance = Scene->createInstance ("gnu.shape");
		entity.Skeleton = Scene->createSkeleton ("gnu.skel");
		// use the instance on the skeleton
		entity.Skeleton.bindSkin (entity.Instance);

		// Allow the skeleton to cast shadows.
		entity.Skeleton.enableCastShadowMap(true);

		entity.Instance.hide ();

		entity.Angle = MouseListener->getOrientation();

		// setup final parameters
		entity.Speed = PlayerSpeed;
		entity.Particule = Scene->createInstance ("appear.ps");
		entity.setState (CEntity::Appear);
		playAnimation (entity, LogInAnim);
		playAnimation (entity, IdleAnim);

		break;
	case CEntity::Other:
		entity.MovePrimitive = MoveContainer->addCollisionablePrimitive(0, 1);
		entity.MovePrimitive->setPrimitiveType(UMovePrimitive::_2DOrientedCylinder);
		entity.MovePrimitive->setReactionType(UMovePrimitive::Slide);
		entity.MovePrimitive->setTriggerType(UMovePrimitive::NotATrigger);
		entity.MovePrimitive->setCollisionMask(OtherCollisionBit+SelfCollisionBit+SnowballCollisionBit);
		entity.MovePrimitive->setOcclusionMask(OtherCollisionBit);
		entity.MovePrimitive->setObstacle(true);
		entity.MovePrimitive->setRadius(1.0f);
		entity.MovePrimitive->setHeight(1.8f);
		entity.MovePrimitive->insertInWorldImage(0);
		entity.MovePrimitive->setGlobalPosition(CVectorD(startPosition.x, startPosition.y, startPosition.z), 0);

		entity.Instance = Scene->createInstance ("gnu.shape");
		entity.Skeleton = Scene->createSkeleton ("gnu.skel");
		entity.Skeleton.bindSkin (entity.Instance);
		entity.Instance.hide ();

		entity.Speed = PlayerSpeed;
		entity.Particule = Scene->createInstance ("appear.ps");
		entity.setState (CEntity::Appear);
		playAnimation (entity, LogInAnim);
		playAnimation (entity, IdleAnim);

		break;
	case CEntity::Snowball:
		entity.MovePrimitive = NULL;

		// allows collision snapping to the ceiling
		entity.VisualCollisionEntity->setCeilMode(true);

		entity.Instance = Scene->createInstance ("snowball.shape");
		entity.Skeleton = NULL;
		entity.Speed = SnowballSpeed;

		// -- -- riiiiight
//#ifdef NL_OS_WINDOWS
//		playSound (entity, SoundId);
//#endif
		entity.setState (CEntity::Normal);
		break;
	}

	if (!entity.Skeleton.empty())
		entity.Skeleton.setPos (startPosition);

	entity.Instance.setPos (startPosition);

// todo sound
//	if (entity.Source != NULL)
//		entity.Source->setPosition (startPosition);

	if (!entity.Particule.empty())
		entity.Particule.setPos (startPosition);

}

// effectively remove the entity (don't play animation)
void deleteEntity (CEntity &entity)
{
	if (!entity.Particule.empty())
	{
		Scene->deleteInstance (entity.Particule);
		entity.Particule = NULL;
	}

	deleteAnimation (entity);

	if (!entity.Skeleton.empty())
	{
		entity.Skeleton.detachSkeletonSon (entity.Instance);
		Scene->deleteSkeleton (entity.Skeleton);
		entity.Skeleton = NULL;
	}

	if (!entity.Instance.empty())
	{
		Scene->deleteInstance (entity.Instance);
		entity.Instance = NULL;
	}

	if (entity.VisualCollisionEntity != NULL)
	{
		VisualCollisionManager->deleteEntity (entity.VisualCollisionEntity);
		entity.VisualCollisionEntity = NULL;
	}

	if (entity.MovePrimitive != NULL)
	{
		MoveContainer->removePrimitive(entity.MovePrimitive);
		entity.MovePrimitive = NULL;
	}

//#ifdef NL_OS_WINDOWS
//	deleteSound (entity);
//#endif

//	nlinfo ("Remove the entity %u from the Entities list", entity.Id);
	EIT eit = findEntity (entity.Id);
	Entities.erase (eit);
}


// Remove an entity specified by its id
// The entity passes into the Disappear state
void removeEntity (uint32 eid)
{
//	nlinfo ("removing entity %u", eid);

	// look for the entity
	EIT eit = findEntity (eid);
	CEntity	&entity = (*eit).second;

	// if there is a particle system linked, delete it
	if (!entity.Particule.empty())
	{
		Scene->deleteInstance (entity.Particule);
		entity.Particule = NULL;
	}

//	if (entity.Type == CEntity::Other)
	{

		entity.Particule = Scene->createInstance("disappear.ps");
		entity.Particule.setPos (entity.Position);
	}

	playAnimation (entity, LogOffAnim, true);
	entity.setState (CEntity::Disappear);
}

void	removeAllEntitiesExceptUs ()
{
	EIT		eit, nexteit;

	// move entities
	for (eit = Entities.begin (); eit != Entities.end (); )
	{
		nexteit = eit; nexteit++;

		CEntity	&entity = (*eit).second;

		if (entity.Type != CEntity::Self)
		{
			// effectively remove the entity (don't play animation)
			deleteEntity (entity);
		}

		eit = nexteit;
	}
}

void deleteAllEntities()
{
	EIT eit, nexteit;
	for (eit = Entities.begin(); eit != Entities.end(); )
	{
		nexteit = eit; nexteit++;
		CEntity	&entity = (*eit).second;
		deleteEntity (entity);
		eit = nexteit;
	}
	Self = NULL;
}





// What to do when the entity appears
void stateAppear (CEntity &entity)
{
	// after 1 second, show the instance
	if (CTime::getLocalTime () > entity.StateStartTime + 1000)
	{
		if (entity.Instance.getVisibility () != UTransform::Show)
			entity.Instance.show ();
	}

	// after 5 seconds, delete the particle system (if any)
	// and pass the entity into the Normal state
	if (CTime::getLocalTime () > entity.StateStartTime + 3000)
	{
		if (!entity.Particule.empty())
		{
			Scene->deleteInstance (entity.Particule);
			entity.Particule = NULL;
		}

		entity.setState (CEntity::Normal);
	}

	if (entity.MovePrimitive != NULL)
		entity.MovePrimitive->move(CVector(0,0,0), 0);
}

// What to do when the entity disappears
void stateDisappear (CEntity &entity)
{
	// after 1 second, remove the mesh and all collision stuff
	if (CTime::getLocalTime () > entity.StateStartTime + 1000)
	{
		if (entity.Instance.getVisibility () != UTransform::Hide)
		{
			if (entity.Type == CEntity::Self)
			{
				if (Self == NULL)
					nlerror("Self entity doesn't exist");
				Self = NULL;
			}

			entity.Instance.hide ();
		}
	}

	// after 5 seconds, remove the particle system and the entity entry
	if (CTime::getLocalTime () > entity.StateStartTime + 3000)
	{
		deleteEntity (entity);
	}
	else
	{
		if (entity.MovePrimitive != NULL)
			entity.MovePrimitive->move(CVector(0,0,0), 0);
	}
}

void stateNormal (CEntity &entity)
{
	double	dt = LocalTimeDelta;
	CVector	oldPos;
	CVector	newPos;

	oldPos = entity.Position;
	CVector	pDelta = entity.Position - entity.ServerPosition;
	CVector	pDeltaOri = pDelta;
	pDelta.z = 0.0f;

	// -- -- simple random bots =)  share with server

	// find a new random server position
	if (entity.Type == CEntity::Other && entity.AutoMove)
	{
		switch (entity.BotState)
		{
		case 0:
			// choose something to do
			if (frand(1.0f) > 0.5f)
				entity.BotState = 5;
			else
				entity.BotState = 2;
			break;
		case 1:
			// walk
			if (pDelta.norm() < 0.1f || LocalTime - entity.BotStateStart > 3.000)
			{
				// reached the point
				entity.BotState = 0;
			}
			else
			{
//				entity.IsWalking = true;
//				entity.IsAiming = false;
			}
			break;
		case 2:
			// aim
			entity.IsWalking = false;
			entity.IsAiming = true;
			entity.BotStateStart = LocalTime;
			entity.BotState = 3;
			break;
		case 3:
			// wait to shoot
			entity.IsWalking = false;
			entity.IsAiming = true;
			if (LocalTime - entity.BotStateStart > 1.000)
			{
				entity.BotState = 4;
				entity.BotStateStart = LocalTime;
				CVector	AimingPosition = entity.Position+CVector(0.0f, 0.0f, 2.0f);
				CVector	direction = CVector((float)(cos(entity.Angle)), (float)(sin(entity.Angle)), 0.3f).normed();
				CVector	AimedTarget = getTarget(AimingPosition,
												direction,
												100);
				shotSnowball(NextEID++, entity.Id, AimingPosition, AimedTarget, SnowballSpeed, 3.0f);
			}
			break;
		case 4:
			// shoot
			entity.IsWalking = false;
			entity.IsAiming = false;
			if (LocalTime - entity.BotStateStart > 1.000)
			{
				entity.BotState = 5;
				entity.BotStateStart = LocalTime;
			}
			break;
		case 5:
			// choose a direction
			float EntityMaxSpeed = 10.0f;
			entity.AuxiliaryAngle += frand(1.5f)-0.75f;
			entity.ServerPosition += CVector((float)cos(entity.AuxiliaryAngle),
											 (float)sin(entity.AuxiliaryAngle),
											 0.0f)*EntityMaxSpeed;
			entity.BotState = 1;
			entity.BotStateStart = LocalTime;
			break;
		}
	}


	if (entity.Type == CEntity::Snowball && LocalTime >= entity.Trajectory.getStopTime())
	{
/*
		CVector	tp(1140,-833,30);
		nlinfo("dist=%f", (entity.Position-tp).norm());
		if ((entity.Position-tp).norm()<30.0f)
		{
			static UInstance t = NULL;
			if (t != NULL)
			{
				Scene->deleteInstance (t);
			}
			t = Scene->createInstance("pills.ps");
			t->setScale (10,10,10);
			CVector tp2 = tp;
			tp2.z+=20;
			t->setPos (tp2);
			nlinfo("touche");
		}
*/
		removeEntity(entity.Id);
	}



	// control the character animation
	if (entity.Type != CEntity::Snowball)
	{
		if (entity.IsAiming && !entity.WasAiming)
		{
			// start aiming
			playAnimation (entity, PrepareSnowBall, true);
			playAnimation (entity, PrepareSnowBallCycle, false);
		}
		else if (entity.WasAiming && !entity.IsAiming)
		{
/*			// end aiming
			playAnimation (entity, ThrowSnowball, true);

			if (entity.IsWalking)
				playAnimation (entity, WalkAnim);
			else
				playAnimation (entity, IdleAnim);
*/		}
		else if (entity.WasAiming && entity.IsAiming)
		{
			// currently aiming => do northing
		}
		else if (!entity.WasWalking && entity.IsWalking)
		{
			playAnimation (entity, PrepareWalkAnim, true);
			playAnimation (entity, WalkAnim);
		}
		else if (entity.WasWalking && !entity.IsWalking)
		{
			playAnimation (entity, IdleAnim, true);
		}

		entity.WasAiming = entity.IsAiming;
		entity.WasWalking = entity.IsWalking;
	}


	entity.ImmediateSpeed = CVector::Null;

	if (entity.Type == CEntity::Self)
	{
		// get new position
		newPos = MouseListener->getPosition();
		// get new orientation
		entity.Angle = MouseListener->getOrientation();

		// Interpolate the character orientation towards the server angle
		// for smoother movements
		float	sweepDistance = entity.AuxiliaryAngle-entity.InterpolatedAuxiliaryAngle;
		if (sweepDistance > (float)Pi)
		{
			sweepDistance -= (float)Pi*2.0f;
			entity.InterpolatedAuxiliaryAngle += (float)Pi*2.0f;
		}
		if (sweepDistance < -(float)Pi)
		{
			sweepDistance += (float)Pi*2.0f;
			entity.InterpolatedAuxiliaryAngle -= (float)Pi*2.0f;
		}
		float	sweepAngle = 4.0f*sweepDistance;
		entity.InterpolatedAuxiliaryAngle += (float)(sweepAngle*dt);
		if ((entity.AuxiliaryAngle-entity.InterpolatedAuxiliaryAngle)*sweepAngle <= 0.0)
			entity.InterpolatedAuxiliaryAngle = entity.AuxiliaryAngle;
		entity.Angle += entity.InterpolatedAuxiliaryAngle;

		// tell the move container how much the entity should move
		if (entity.IsWalking)
		{
			entity.ImmediateSpeed = (newPos-oldPos)/(float)dt;
			if (_TestCLS) entity.MovePrimitive->setGlobalPosition(newPos, 0);
			else entity.MovePrimitive->move(entity.ImmediateSpeed, 0);
		}
	}
	else if (entity.Type == CEntity::Other)
	{
		// go to the server position with linear interpolation
		// -- -- useful for speed limiting on frontend service
		// -- -- random note: also, get rid of the position service, 
		//       and move the snowball physics to a more useful service

		// Interpolate orientation for smoother motions
		// AuxiliaryAngle -> the server imposed angle
		// InterpolatedAuxiliaryAngle -> the angle showed by the entity
		float	sweepDistance = entity.AuxiliaryAngle-entity.InterpolatedAuxiliaryAngle;
		if (sweepDistance > (float)Pi)
		{
			sweepDistance -= (float)Pi*2.0f;
			entity.InterpolatedAuxiliaryAngle += (float)Pi*2.0f;
		}
		if (sweepDistance < -(float)Pi)
		{
			sweepDistance += (float)Pi*2.0f;
			entity.InterpolatedAuxiliaryAngle -= (float)Pi*2.0f;
		}
		float	sweepAngle = 4.0f*sweepDistance;
		entity.InterpolatedAuxiliaryAngle += (float)(sweepAngle*dt);
		if ((entity.AuxiliaryAngle-entity.InterpolatedAuxiliaryAngle)*sweepAngle <= 0.0)
			entity.InterpolatedAuxiliaryAngle = entity.AuxiliaryAngle;

		entity.Angle = entity.InterpolatedAuxiliaryAngle;

//		if (entity.IsWalking)
		if (pDelta.norm() > 0.1f)
		{
			pDelta.normalize();
			entity.ImmediateSpeed = pDelta*(-entity.Speed);
			entity.MovePrimitive->move(entity.ImmediateSpeed, 0);
			entity.IsWalking = true;
		}
		else
		{
			entity.IsWalking = false;
		}
	}
	else if (entity.Type == CEntity::Snowball)
	{
		// go to the server position using trajectory interpolation
		CVector newPos = entity.Trajectory.eval(LocalTime);
		if (newPos != entity.Position)
		{
			entity.Position = entity.Trajectory.eval(LocalTime);
			entity.Instance.show ();
		}
	}
	else
	{
		// automatic speed
		newPos = oldPos;
	}
}





void updateEntities ()
{
	// compute the delta t that has elapsed since the last update (in seconds)
	double	dt = LocalTimeDelta;
	EIT		eit, nexteit;

	// move entities
	for (eit = Entities.begin (); eit != Entities.end (); )
	{
		nexteit = eit; nexteit++;

		CEntity	&entity = (*eit).second;

		switch (entity.State)
		{
		case CEntity::Appear:
			stateAppear (entity);
			break;
		case CEntity::Normal:
			stateNormal (entity);
			break;
		case CEntity::Disappear:
			stateDisappear (entity);
			break;
		default:
			nlstop;
			break;
		}

		eit = nexteit;
	}

	// evaluate collisions
	MoveContainer->evalCollision(dt, 0);

	// snap entities to the ground
	for (eit = Entities.begin (); eit != Entities.end (); eit++)
	{
		CEntity	&entity = (*eit).second;
		UGlobalPosition	gPos;

		if (entity.MovePrimitive != NULL)
		{
			// get the global position in pacs coordinates system
			entity.MovePrimitive->getGlobalPosition(gPos, 0);
			// convert it in a vector 3d
			entity.Position = GlobalRetriever->getGlobalPosition(gPos);
			// get the good z position
			gPos.LocalPosition.Estimation.z = 0.0f;
			entity.Position.z = GlobalRetriever->getMeanHeight(gPos);

			// check position retrieving
/*
			UGlobalPosition gPosCheck;
			gPosCheck = GlobalRetriever->retrievePosition(entity.Position);
			if (gPos.InstanceId != gPosCheck.InstanceId ||
				gPos.LocalPosition.Surface != gPosCheck.LocalPosition.Surface)
			{
				nlwarning("Checked UGlobalPosition differs from store");
//				gPos.InstanceId = gPosCheck.InstanceId;
//				gPos.LocalPosition.Surface = gPosCheck.LocalPosition.Surface;
			}
*/
			// snap to the ground
			if (!GlobalRetriever->isInterior(gPos))
				entity.VisualCollisionEntity->snapToGround(entity.Position);

			if (entity.Type == CEntity::Other &&
				(entity.ServerPosition-entity.Position)*entity.ImmediateSpeed < 0.0f)
			{
//				nlinfo("detected over entity %d", entity.Id);
				entity.ServerPosition.z = entity.Position.z;
				entity.Position = entity.ServerPosition;
				if (!GlobalRetriever->isInterior(gPos))
					entity.VisualCollisionEntity->snapToGround(entity.Position);
				entity.MovePrimitive->setGlobalPosition(CVectorD(entity.Position.x, entity.Position.y, entity.Position.z), 0);
			}

		}

		if (!entity.Instance.empty())
		{
			CVector	jdir;
			switch (entity.Type)
			{
			case CEntity::Self:
				jdir = CVector(-(float)cos(entity.Angle), -(float)sin(entity.Angle), 0.0f);
				break;
			case CEntity::Other:
				jdir = CVector(-(float)cos(entity.Angle), -(float)sin(entity.Angle), 0.0f);
				break;
			case CEntity::Snowball:
				jdir = entity.Trajectory.evalSpeed(LocalTime).normed();
				break;
			}

			if (!entity.Skeleton.empty())
			{
				entity.Skeleton.setPos(entity.Position);
				entity.Skeleton.setRotQuat(jdir);
			}

			entity.Instance.setPos(entity.Position);
			entity.Instance.setRotQuat(jdir);
		}

// todo sound
//		if (entity.Source != NULL)
//			entity.Source->setPosition (entity.Position);

		if (!entity.Particule.empty())
		{
			entity.Particule.setPos(entity.Position);
		}

		if (entity.Type == CEntity::Self)
		{
			MouseListener->setPosition(entity.Position);
		}
	}
}

// Draw entities names up the characters
void renderEntitiesNames ()
{
	// Setup the driver in matrix mode
	Driver->setMatrixMode3D (Camera);
	// Setup the drawing context
	TextContext->setHotSpot (UTextContext::MiddleTop);
	TextContext->setColor (EntityNameColor);
	TextContext->setFontSize ((uint32)EntityNameSize);
	//
	for (EIT eit = Entities.begin (); eit != Entities.end (); eit++)
	{
		CEntity	&entity = (*eit).second;
		if (!entity.Instance.empty() && entity.Type == CEntity::Other)
		{
			CMatrix		mat = Camera.getMatrix();
			mat.setPos(entity.Position + CVector(0.0f, 0.0f, 4.0f));
			mat.scale(10.0f);
			TextContext->render3D(mat, entity.Name);
		}
	}
}


// The entities preferences callback
void cbUpdateEntities (CConfigFile::CVar &var)
{
	if (var.Name == "EntityNameColor") EntityNameColor.set (var.asInt(0), var.asInt(1), var.asInt(2), var.asInt(3));
	else if (var.Name == "EntityNameSize") EntityNameSize = var.asFloat ();
	else nlwarning ("Unknown variable update %s", var.Name.c_str());
}

void initEntities()
{
	ConfigFile->setCallback ("EntityNameColor", cbUpdateEntities);
	ConfigFile->setCallback ("EntityNameSize", cbUpdateEntities);

	cbUpdateEntities (ConfigFile->getVar ("EntityNameColor"));
	cbUpdateEntities (ConfigFile->getVar ("EntityNameSize"));
}

void releaseEntities()
{
	// Remove config file callbacks
	ConfigFile->setCallback("EntityNameColor", NULL);
	ConfigFile->setCallback("EntityNameSize", NULL);

	// Delete all entities (should already have been called normally)
	deleteAllEntities();
}


// Reset the pacs position of an entity, in case pacs went wrong
void	resetEntityPosition(uint32 eid)
{
	uint32 sbid = NextEID++;
	EIT eit = findEntity (eid);

	CEntity	&entity = (*eit).second;

	UGlobalPosition	gPos;
	// get the global position
	gPos = GlobalRetriever->retrievePosition(entity.Position);
	// convert it in a vector 3d
	entity.Position = GlobalRetriever->getGlobalPosition(gPos);
	// get the good z position
	gPos.LocalPosition.Estimation.z = 0.0f;
	entity.Position.z = GlobalRetriever->getMeanHeight(gPos);

	// snap to the ground
	if (entity.VisualCollisionEntity != NULL && !GlobalRetriever->isInterior(gPos))
		entity.VisualCollisionEntity->snapToGround(entity.Position);

	if (entity.MovePrimitive != NULL)
		entity.MovePrimitive->setGlobalPosition(CVector(entity.Position.x, entity.Position.y, entity.Position.z), 0);
}


void	shotSnowball(uint32 sid, uint32 eid, const CVector &start, const CVector &target, float speed, float deflagRadius)
{
	// get direction
	CVector direction = (target-start).normed();

	// create a new snowball entity
	addEntity(sid, "", CEntity::Snowball, start, target);
	EIT sit = findEntity (sid);
	CEntity	&snowball = (*sit).second;

	snowball.AutoMove = 1;
	snowball.Trajectory.init(start, target, speed, LocalTime + 1.000);
	snowball.Instance.hide();

	EIT eit = findEntity (eid);
	CEntity	&entity = (*eit).second;

	// end aiming
	playAnimation (entity, ThrowSnowball, true);

	if (entity.IsWalking)
	{
		playAnimation (entity, PrepareWalkAnim, true);
		playAnimation (entity, WalkAnim);
	}
	else
		playAnimation (entity, IdleAnim);

}


//
// Commands
//

NLMISC_COMMAND(remove_entity,"remove a local entity","<eid>")
{
	// check args, if there s not the right number of parameter, return bad
	if(args.size() != 1) return false;

	uint32 eid = (uint32)atoi(args[0].c_str());
	removeEntity (eid);

	return true;
}


NLMISC_COMMAND(add_entity,"add a local entity","<nb_entities> <auto_update>")
{
	// check args, if there s not the right number of parameter, return bad
	if(args.size() != 2) return false;

	uint nb = (uint)atoi(args[0].c_str());

	for (uint i = 0; i < nb ; i++)
	{
		uint32 eid = NextEID++;
		CVector start(ConfigFile->getVar("StartPoint").asFloat(0), ConfigFile->getVar("StartPoint").asFloat(1), ConfigFile->getVar("StartPoint").asFloat(2));
		addEntity (eid, "Entity"+toString(eid), CEntity::Other, start, start);
		EIT eit = findEntity (eid);
		(*eit).second.AutoMove = atoi(args[1].c_str()) == 1;
	}

	return true;
}

NLMISC_COMMAND(speed,"set the player speed","[<entity_id>] <speed in km/h>")
{
	// check args, if there s not the right number of parameter, return bad
	if(args.size() == 1)
	{
		float speed = min( max( (float)atof(args[0].c_str()), 0.1f ), 200.0f ); // speed range in km/h
		if (Self != NULL)
		{
			MouseListener->setSpeed( speed / 3.6f );
			Self->Speed = speed / 3.6f;
		}
	}
	else if(args.size() == 2)
	{
		float speed = min( max( (float)atof(args[1].c_str()), 0.1f ), 200.0f ); // speed range in km/h

		uint eid = (uint)atoi(args[0].c_str());
		EIT eit = findEntity (eid);
		CEntity	&entity = (*eit).second;

		entity.Speed = speed / 3.6f;
		if (entity.Type == CEntity::Self)
		{
			MouseListener->setSpeed(entity.Speed);
		}
	}
	return true;
}

NLMISC_COMMAND(goto, "go to a given position", "<x> <y>")
{
	// check args, if there s not the right number of parameter, return bad
	if(args.size() != 2) return false;

	if (Self == NULL) return false;

	CEntity	&entity = *Self;

	float	x, y;

	x = (float)atof(args[0].c_str());
	y = (float)atof(args[1].c_str());

	//
	if (entity.MovePrimitive != NULL && entity.VisualCollisionEntity != NULL)
	{
		UGlobalPosition	gPos;
		entity.MovePrimitive->setGlobalPosition(CVectorD(x, y, 0.0f), 0);
		// get the global position in pacs coordinates system
		entity.MovePrimitive->getGlobalPosition(gPos, 0);
		// convert it in a vector 3d
		entity.Position = GlobalRetriever->getGlobalPosition(gPos);
		// get the good z position
		gPos.LocalPosition.Estimation.z = 0.0f;
		entity.Position.z = GlobalRetriever->getMeanHeight(gPos);
		// snap to the ground
		if (!GlobalRetriever->isInterior(gPos))
		   entity.VisualCollisionEntity->snapToGround( entity.Position );

		if (entity.Type == CEntity::Self)
		{
			MouseListener->setPosition(entity.Position);
		}
	}

	return true;
}

NLMISC_COMMAND(entities, "display all entities info", "")
{
	// check args, if there s not the right number of parameter, return bad
	if(args.size() != 0) return false;

	for (EIT eit = Entities.begin (); eit != Entities.end (); eit++)
	{
		CEntity	&e = (*eit).second;
		log.displayNL("%s %u (k%u) %s %d", (Self==&e)?"*":" ", e.Id, (*eit).first, e.Name.c_str(), e.Type);
	}
	return true;
}

NLMISC_COMMAND(test_cls, "test the collision service, disables collision test on self", "")
{
	_TestCLS = !_TestCLS;
	return true;
}

} /* namespace SBCLIENT */

/* end of file */