/*
   core/entity.cc
   This file is part of the Osirion project and is distributed under
   the terms of the GNU General Public License version 2.
*/

#include <vector>
#include <iomanip>
#include <cstring>
#include <cassert>

#include "auxiliary/functions.h"
#include "sys/sys.h"
#include "core/entity.h"
#include "core/cvar.h"
#include "core/application.h"
#include "core/gameinterface.h"
#include "core/gameserver.h"

#include "BulletCollision/CollisionShapes/btBoxShape.h"

namespace core
{

// maximal number of entities
const size_t 	MAX_ENTITY 		= 1048574;
// minimal speed
const float 	SPEED_THRESHOLD 	= 0.001f;

using math::Color;
using math::Vector3f;

/* ---- Static functions for the Entity registry ------------------- */

const InfoType *Entity::entity_infotype = 0;

Entity::Registry Entity::entity_registry;
size_t Entity::entity_nextid = 0;

void Entity::add(Entity *ent)
{
	Registry::iterator it;
	entity_nextid = (entity_nextid % MAX_ENTITY) + 1; // lowest entity-id is 1
	unsigned int id = entity_nextid;
	for (it = entity_registry.begin(); it != entity_registry.end() && id == (*it).second->id(); it++) {
		id++;
	}
	ent->entity_id = id;
	entity_registry[id] = ent;
}

void Entity::add(Entity *ent, unsigned int id)
{
	if (find(id)) {
		con_warn << "Duplicate entity " << id << "!\n";
		return;
	}
	ent->entity_id = id;
	entity_registry[id] = ent;
}

Entity *Entity::find(unsigned int id)
{
	Registry::iterator it = entity_registry.find(id);
	if (it == entity_registry.end()) {
		return 0;
	} else {
		return (*it).second;
	}
}

Entity *Entity::find(Entity * entity)
{
	for (Registry::iterator it = entity_registry.begin(); it != entity_registry.end(); ++it) {
		if ((*it).second == entity) {
			return entity;
		}
	}
	return 0;
}


void Entity::erase(unsigned int id)
{
	Registry::iterator it = entity_registry.find(id);
	if (it != entity_registry.end()) {
		delete((*it).second);
		(*it).second = 0;
		entity_registry.erase(it);
	} else {
		con_warn << "Could not erase entity " << id << "!\n";
	}
}

void Entity::clear()
{
	for (Entity::Registry::iterator it = Entity::registry().begin(); it != Entity::registry().end(); it++) {
		delete(*it).second;
	}
	Entity::registry().clear();
	entity_nextid = 0;
}

void Entity::list_header()
{
	con_print << "  "
		<< "  id "
		<< "type      "
		<< "label                    "
		<< "name" << std::endl;
}

void Entity::list(const Entity *entity)
{
	con_print << "  "
		<< "^B" << std::setw(4) << entity->id() << " "
		<< "^N" << std::setfill('0') << std::setw(4) << entity->type() <<  ":" << std::setw(4) << entity->moduletype() << std::setfill(' ') << " "
		<< "^N" << aux::pad_right(entity->label(), 24) << " "
		<< "^N" << entity->name() << std::endl;
}

void Entity::list()
{
	list_header();
	for (Registry::iterator it = entity_registry.begin(); it != entity_registry.end(); it++) {
		std::string typeindicator;
		Entity *entity = (*it).second;
		list(entity);
	}
	con_print << "^B  " << entity_registry.size() << " entities" << std::endl;
}

/* ---- class Entity ----------------------------------------------- */

Entity::Entity() :
		entity_location(0.0f, 0.0f, 0.0f),
		entity_color(1.0f, 1.0f, 1.0f, 1.0f),
		entity_color_second(1.0f, 1.0f, 1.0f, 1.0f)
{
	entity_id = 0;
	entity_flags = 0;
	entity_moduletypeid = 0;

	entity_model = 0;

	entity_body = 0;
	entity_body_info = 0;
	entity_collision_shape = 0;
	entity_speed = 0.0f;

	entity_radius = 0.5f;
	entity_shape = Diamond;

	entity_created = true;
	entity_died = false;
	entity_dirty = false;
	entity_keepalive = 0;

	entity_zone = 0;
	entity_oldzone = 0;
	entity_visible = true;

	entity_serverside = false;
	
	entity_inventory = 0;
	entity_slots = 0;
	entity_info = 0;
	
	entity_faction = 0;

	memset(entity_extension, 0, sizeof(entity_extension));

	add(this);
}

Entity::Entity(std::istream & is)
{
	entity_serverside = false;
	entity_id = 0;
	entity_zone = 0;
	entity_oldzone = 0;
	entity_visible = true;
	entity_keepalive = 0;

	entity_model = 0;

	entity_body = 0;
	entity_body_info = 0;
	entity_collision_shape = 0;
	entity_speed = 0.0f;

	entity_created = true;
	entity_died = false;
	
	entity_inventory = 0;
	entity_slots = 0;
	entity_info = 0;
	
	entity_faction = 0;

	memset(entity_extension, 0, sizeof(entity_extension));
}

Entity::~Entity()
{
	// delete extensions
	for (size_t i = 0; i < 4; i++) {
		if (entity_extension[i])
			delete entity_extension[i];
		entity_extension[i] = 0;
	}

	// delete entity menus
	for (Menus::iterator mit = menus().begin(); mit != menus().end(); mit++) {
		delete (*mit);
		(*mit) = 0;
	}
	menus().clear();
	
	// delete equipment slots
	if (entity_slots) {
		delete entity_slots;
	}
	
	// delete inventory
	if (entity_inventory) {
		delete entity_inventory;
	}

	// remove entity from zone
	if (entity_zone) {
		entity_zone->remove(this);
	
		if (body() && entity_zone->physics()) {
			entity_zone->physics()->removeRigidBody(body());
		}
	}
	
	// delete collision shape
	if (entity_collision_shape) {
		delete entity_collision_shape;
		entity_collision_shape = 0;
	}
	
	// delete child collision shapes
	for (CollisionShapes::iterator sit = entity_collision_child_shapes.begin(); sit != entity_collision_child_shapes.end(); sit++) {
		delete (*sit);
		(*sit) = 0;
	}
	entity_collision_child_shapes.clear();
	
	// delete collision body
	if (entity_body) {
		delete entity_body;
	}
	
	// delete collision body construction information
	if (entity_body_info) {
		delete entity_body_info;
	}
}

void Entity::print() const
{
	// print header
	print_header();
	
	// type
	con_print << "  ^Ntype        ^B";
	switch (type()) {
		case Default:
			con_print << "entity";
			break;
			
		case Dynamic:
			con_print << "dynamic";
			break;
			
		case Controlable:
			con_print << "controlable";
			break;
			
		case Globe:
			con_print << "globe";
			break;
			
		case Projectile:
			con_print << "projectile";
	}
	con_print << std::endl;
	con_print << "  ^Nmodule type ^B" << moduletype() << std::endl;
	
	// print entity flags
	con_print << "  ^Nflags      ^B";
	if (has_flag(NonSolid)) {
		con_print << " nonsolid";
	}
	if (has_flag(Bright)) {
		con_print << " bright";
	}
	if (has_flag(Dockable)) {
		con_print << " dockable";
	}
	if (has_flag(ShowOnMap)) {
		con_print << " shownmap";
	}
	if (has_flag(KeepAlive)) {
		con_print << " keepalive";
	}
	con_print << std::endl;	
	if (inventory()) {
		con_print << "  ^Ninventory   ^Byes" << std::endl;
	}	
	if (slots()) {
		con_print << "  ^Nslots       ^Byes" << std::endl;
	}	
	if (faction()) {
		con_print << "  ^Nfaction     ^B" << faction()->label() << std::endl;
	}
}

void Entity::print_header() const
{
	con_print << "  entity id ^B" << id() << " ^Nlabel ^B" << label() << " ^Nname ^B" << name();
	if (zone()) {
		con_print << " ^Nzone ^B" << zone()->label();
	}
	con_print << std::endl;	
}

void Entity::print_inventory() const
{	
	if (!entity_inventory) {
		con_print << "no inventory availble" << std::endl;
		return;
	}
	
	con_print << "  ^Nitem infotype info                   amount flags" << std::endl;
	for (Inventory::Items::const_iterator it = entity_inventory->items().begin(); it != entity_inventory->items().end(); it++) {
		Item *item = (*it);
		// TODO item flags
		con_print << " " 
			  << " ^B" << std::setw(4) << item->id()
			  << " ^N" << aux::pad_right((item->info()->type() ? item->info()->type()->label() : "NULL"), 8)
			  << " ^N" << aux::pad_right(item->info()->label(), 24)
			  << std::setw(5) << item->amount();
		if (item->has_flag(core::Item::Tradeable)) {
			con_print << " tradeable";
		}
		if (item->has_flag(core::Item::Unique)) {
			con_print << " unique";
		}
		if (item->has_flag(core::Item::Unrestricted)) {
			con_print << " unrestricted";
		}
		if (item->has_flag(core::Item::Mounted)) {
			con_print << " mounted";
		}
		con_print<< std::endl;
	}
	con_print << "^B  " << entity_inventory->items().size() << " inventory items" << std::endl;
}

void Entity::die()
{
	entity_died = true;
}

void Entity::clear_updates()
{
	entity_created = false;
	entity_dirty = false;
	entity_oldzone = 0;
	
	if (entity_inventory && entity_inventory->dirty()) {
		// inventory timestamp must be set for singleplayer
		entity_inventory->set_timestamp(game()->timestamp());
		entity_inventory->set_dirty(false);
	}
}

void Entity::set_info(const Info *info)
{
	entity_info = info;
}

Inventory *Entity::add_inventory()
{
	if (!entity_inventory) {
		entity_inventory = new Inventory();
	}	
	return(entity_inventory);	
}

Slots *Entity::add_slots()
{
	if (!entity_slots) {
		entity_slots = new Slots();
	}
	return entity_slots;
}

void Entity::set_zone(Zone *zone)
{
	if (entity_zone == zone)
		return;

	if (entity_zone) {
		entity_zone->remove(this);
		if (body() && entity_zone->physics()) {
			entity_zone->physics()->removeRigidBody(body());
		}
	}

	// oldzone is cleared after every game frame
	if (!entity_oldzone)
		entity_oldzone = entity_zone;

	entity_zone = zone;
	entity_dirty = true;

	if (entity_zone) {
		entity_zone->add(this);
		if (body() && entity_zone->physics()) {
			entity_zone->physics()->addRigidBody(body());
			reset();
		}
	}
}

void Entity::set_model(model::Model *model)
{
	// server-side property should not clear modelname
	entity_model = model;
	if (entity_model) {
		entity_modelname.assign(entity_model->name());
	}
}

void Entity::set_modelname(const std::string &modelname)
{
	if (!modelname.size()) {
		set_model(0);
	} else {
		set_model(model::Model::load(modelname));
	}

	if (!entity_model)
		entity_modelname.clear();
}

void Entity::serialize_server_create(std::ostream & os) const
{
	os << moduletype() << " "
	<< flags() << " "
	<< (visible() ? 1 : 0) << " "
	<< (zone() ? zone()->id() : 0) << " "
	<< std::setprecision(8) << entity_location << " "
	<< color() << " "
	<< color_second() << " "
	<< shape() << " "
	<< radius() << " "
	<< std::setprecision(8) << entity_axis.forward() << " "
	<< std::setprecision(8) << entity_axis.left() << " "
	<< "\"" <<label() << "\" "
	<< "\"" << name() << "\" "
	<< "\"" << (entity_model ? entity_model->name() : "") << "\" "
	<< (faction() ? faction()->id() : 0) << " "
	<< (info() ? info()->id() : 0) << " "
	<< (inventory() ? 1 : 0) << " ";
	
	if (inventory()) 
		inventory()->serialize_server_update(os);
}

void Entity::receive_server_create(std::istream &is)
{
	unsigned int s;
	unsigned int zo;
	unsigned int o = 0;
	std::string n;
	char c;

	is >> entity_moduletypeid;
	is >> entity_flags;

	is >> o;
	if (o)
		entity_visible = true;
	else
		entity_visible = false;

	is >> zo;

	set_zone(Zone::find(zo));
	if (entity_zone && !zo) {
		con_warn << "Received entity " << entity_id << " for unknown zone " << zo << "!" << std::endl;
	}

	is >> entity_location;
	is >> entity_color;
	is >> entity_color_second;

	is >> s; // shape
	entity_shape = (Shape) s;

	is >> entity_radius;

	is >> entity_axis[0];
	is >> entity_axis[1];
	entity_axis[2] = math::crossproduct(entity_axis.forward(), entity_axis.left());

	// read label
	n.clear();
	while ((is.get(c)) && (c != '"'));
	while ((is.get(c)) && (c != '"'))
		n += c;
	set_label(n);
	
	// read name
	n.clear();
	while ((is.get(c)) && (c != '"'));
	while ((is.get(c)) && (c != '"'))
		n += c;
	set_name(n);
	
	// read model name
	n.clear();
	while ((is.get(c)) && (c != '"'));
	while ((is.get(c)) && (c != '"'))
		n += c;
	set_modelname(n);

	// read faction id
	if(is >> o) {
		entity_faction = Info::find(o);
		if (o && !entity_faction)
			entity_faction = new Info(o);
	} else {
		entity_faction = 0;
	}
	
	// read info id
	if(is >> o) {
		entity_info = Info::find(o);
		if (o && !entity_info)
			entity_info = new Info(o);
	} else {
		entity_info = 0;
	}
	
	// has inventory
	if (is >> o) {
		if (!o) {
			if (inventory()) {
				con_warn << "No inventory received for entity " << id() << " " << label() << "  with inventory!" << std::endl;
				entity_inventory->clear();
			}
		} else {
			if (!inventory()) {
				entity_inventory = new Inventory();
			}
		}
	} else {
		if (inventory()) {
			con_warn << "No inventory received for entity " << id() << " " << label() << "  with inventory!" << std::endl;
			entity_inventory->clear();
		}
	}
	
	if (inventory()) {
		inventory()->receive_server_update(is);
	}
	
	entity_dirty = false;
}

void Entity::serialize_client_update(std::ostream & os) const
{
}

void Entity::receive_client_update(std::istream &is)
{
}

void Entity::serialize_server_update(std::ostream & os) const
{
}

void Entity::receive_server_update(std::istream &is)
{
}

void Entity::upkeep(const unsigned long timestamp)
{
}

void Entity::add_menu(MenuDescription *menu)
{
	entity_menus.push_back(menu);
}

MenuDescription *Entity::find_menu(std::string const &label)
{
	for (Menus::iterator it = menus().begin(); it != menus().end(); it++) {
		if (label.compare((*it)->label()) == 0)
			return (*it);
	}
	return 0;
}

void Entity::remove_menu(std::string const &label)
{
	for (Menus::iterator it = menus().begin(); it != menus().end(); it++) {
		if (label.compare((*it)->label()) == 0) {
			menus().erase(it);
			return;
		}
	}
}


void Entity::frame(const unsigned long elapsed)
{
	if (entity_collision_child_shapes.size() > 0) {
		btCompoundShape *compoundshape = static_cast<btCompoundShape *> (entity_collision_shape);
		const float modelscale = radius() / model()->radius();
		
		for (int n = 0; n < compoundshape->getNumChildShapes(); n++) {
			model::CollisionMesh *mesh = static_cast<model::CollisionMesh *>(compoundshape->getChildShape(n)->getUserPointer());

			if (mesh) {
				if (mesh->type() == model::FragmentGroup::Rotate) {
					const float rotation_radians = math::degrees360f(game()->time() * mesh->speed()) * M_PI / 180.0f;
					math::Axis rotation_axis;
					rotation_axis.rotate(mesh->movement(), rotation_radians);

					btTransform child_transform;
					child_transform.setIdentity();
					child_transform.setOrigin(math::to_btVector3(mesh->location() * modelscale ));
					child_transform.setBasis(math::to_btMatrix3x3(mesh->axis() * rotation_axis));
					
					compoundshape->updateChildTransform(n, child_transform);
			
				} else if (mesh->type() == model::FragmentGroup::Move ) {
					const float freq = mesh->speed() / mesh->distance(); 
					math::Vector3f translation(mesh->movement() * mesh->distance());
					translation *= sinf(core::game()->time() * M_PI * freq) * 0.5f + 0.5f;

					btTransform child_transform;
					child_transform.setIdentity();
					child_transform.setOrigin(to_btVector3((mesh->location() + mesh->axis() * translation *  mesh->scale()) *  modelscale));
					child_transform.setBasis(math::to_btMatrix3x3(mesh->axis()));

					compoundshape->updateChildTransform(n, child_transform);
				}
			}
		}
	}
}

void Entity::reset()
{
	if (!radius() || has_flag(NonSolid)) {
		return;
	}
	
	// location and orientation
	btTransform t;
	t.setIdentity();
	t.setOrigin(to_btVector3(location()));
	t.setBasis(to_btMatrix3x3(axis()));
	
	// construct physics body if required
	if (!entity_body) {
		
		// if there is no body, this entity should not have shapes either
		assert (entity_collision_shape == 0);
		assert (entity_collision_child_shapes.size() == 0);
		
		// create collision shape
		if (model() && model()->radius()) {
			const float modelscale = radius() / model()->radius();
			
			if (model()->collisionmodel() && model()->collisionmodel()->size()) {
				
				// a valid collision model has been found, use it to construct a compoundshape
				btCompoundShape *compoundshape = new btCompoundShape();
				for (model::CollisionModel::CollisionMeshes::iterator it = model()->collisionmodel()->meshes().begin();
					it != model()->collisionmodel()->meshes().end(); it++) {
					
					model::CollisionMesh *mesh = (*it);
					
					// generate a btCollisionShape for each model::CollisionMesh in the model::CollisionModel
					// static entities use a btBvhTriangleMeshShape
					btBvhTriangleMeshShape *meshshape = new btBvhTriangleMeshShape(mesh->triangles(), true, true);
					meshshape->setMargin(Cvar::sv_collisionmargin->value());
					
					const float meshscale = mesh->scale() * modelscale;
					const btVector3 meshscalevec(meshscale, meshscale, meshscale);
					
					// TODO 
					// This does not work as advertized, it is actually the underlying mesh that
					// gets scaled. If there are multiple entities with the same model at a different scale,
					// the scale is set per-model instead of per-entity
					// Note that it is still possible to reuse and rescale the same model used by an Entitycontrolable
					// (and most likely, EntityDynamic as well)
					meshshape->setLocalScaling(meshscalevec);
					meshshape->buildOptimizedBvh();
					meshshape->recalcLocalAabb();

					// apply collision mesh properties to the btCollisionShape
					btTransform child_transform;
					child_transform.setIdentity();
					child_transform.setOrigin(to_btVector3(mesh->location() * modelscale));
					child_transform.setBasis(to_btMatrix3x3(mesh->axis()));
					
					switch (mesh->type()) {
						case model::FragmentGroup::Rotate:
						case model::FragmentGroup::Move:
							meshshape->setUserPointer((void *) mesh);
							break;
							
						default:
							meshshape->setUserPointer(0);
							break;
					}
					// add the mesh to the compoundhape and store a pointer to the child shape
					compoundshape->addChildShape(child_transform, meshshape);
					entity_collision_child_shapes.push_back(meshshape);
				}
				
				entity_collision_shape = compoundshape;
				
			} else {
				// model without collision model
				// use bounding box
				btBoxShape *boxshape =  new btBoxShape(to_btVector3(model()->box().max() * modelscale));
				entity_collision_shape = boxshape;
			}
			
		} else {
			// no model
			// use a sphere with a radius matching the entity
			btSphereShape *sphereshape = new btSphereShape(radius());
			entity_collision_shape = sphereshape;
			// TODO the actual shape should depend on Entity::Shape
		}
		
		entity_collision_shape->setMargin(Cvar::sv_collisionmargin->value());
		
		// create physics body
		btVector3 inertia(0, 0, 0);
		entity_body_info = new btRigidBody::btRigidBodyConstructionInfo(0, 0, entity_collision_shape, inertia);
		
		entity_body = new btRigidBody(*entity_body_info);
		//entity_body->setActivationState(ISLAND_SLEEPING);
		entity_body->setActivationState(WANTS_DEACTIVATION);
		entity_body->setCollisionFlags(entity_body->getCollisionFlags() | btCollisionObject::CF_STATIC_OBJECT);
		entity_body->setUserPointer((void *) this);
		
		if (zone())
			zone()->physics()->addRigidBody(entity_body);
	}

	// transfer entity location to body
	body()->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f));
	body()->setAngularVelocity(btVector3(0.0f, 0.0f, 0.0f));
	body()->clearForces();
	body()->setWorldTransform(t);
	
	
	set_dirty();
}

/* ---- class EntityDynamic ---------------------------------------- */

EntityDynamic::EntityDynamic() : Entity()
{
	entity_state = Normal;
	entity_timer = 0;
	entity_motionstate = 0;
	entity_mass = 0.0f;
}

EntityDynamic::EntityDynamic(std::istream & is) : Entity(is)
{
	entity_state = Normal;
	entity_timer = 0;
	entity_motionstate = 0;
	entity_mass = 0.0f;
}

EntityDynamic::~EntityDynamic()
{
	if (entity_motionstate)
		delete entity_motionstate;
}

void EntityDynamic::print() const
{
	Entity::print();
	
	con_print << "  ^Nstate       ^B";
	switch (state()) {
		case Normal:
			con_print << "normal";
			break;

		case NoPower:
			con_print << "nopower";
			break;
			
		case ImpulseInitiate:
			con_print << "impulseinitiate";
			break;
			
		case Impulse:
			con_print << "impulse";
			break;
			
		case JumpInitiate:
			con_print << "jumpinitiate";
			break;
			
		case Jump:
			con_print << "jump";
			break;
			
		case Docked:
			con_print << "docked";
			break;
			
		case Destroyed:
			con_print << "destroyed";
			break;			
	}
	con_print << std::endl;
}

void EntityDynamic::set_state(int state)
{
	if (this->state() != state) {
		entity_state = state;
		set_dirty();
	}
}

void EntityDynamic::nudge(const bool randomize)
{
	if (!zone()) {
		return;
	}
	
	for (Zone::Content::const_iterator it = zone()->content().begin(); it != zone()->content().end(); it++) {
		const Entity *other = (*it);
		if ((other->type() != Projectile) && (other != this)) {
			// check distance
			const float d = math::distance(location(), other->location());
			const float r = radius() + other->radius();
			
			if ( d < r ) {
				if (randomize) {
					// nudge into randomize direction
					math::Axis nudge_axis(axis());
					nudge_axis.change_direction(math::randomf(60.0f) - 30.0f);
					nudge_axis.change_pitch(math::randomf(60.0f) - 30.0f);
					set_location(location() + nudge_axis.forward() * (r - d + 0.001f));
				} else {
					set_location(location() + axis().forward() * (r - d + 0.001f));
				}
				// restart search
				it = zone()->content().begin();
			}
		}
	}
}

void EntityDynamic::reset()
{
	// no bullet physics on NonSolid entities
	if (!radius() || has_flag(NonSolid)) {
		return;
	}

	// remove Docked and Destroyed entities from the physics simulation
	if (died() || (state() == core::Entity::Docked) || (state() == core::Entity::Destroyed)) {
		
		if (entity_body) {
		
			if (entity_motionstate) {
				delete entity_motionstate;
				entity_motionstate = 0;
			}
				
			if (zone() && zone()->physics()) {
				entity_zone->physics()->removeRigidBody(body());
			}
			
			if (entity_collision_shape) {
				delete entity_collision_shape;
				entity_collision_shape = 0;
			}
			
			for (CollisionShapes::iterator sit = entity_collision_child_shapes.begin(); sit != entity_collision_child_shapes.end(); sit++) {
				delete (*sit);
				(*sit) = 0;
			}
			entity_collision_child_shapes.clear();
			
			if (entity_body) {
				delete entity_body;
				entity_body = 0;
			}
			
			if (entity_body_info) { 
				delete entity_body_info;
				entity_body_info = 0;
			}
		}
		
		return;
	}
	
	// location and orientation
	btTransform t;
	t.setIdentity();
	t.setOrigin(to_btVector3(location()));
	t.setBasis(to_btMatrix3x3(axis()));
	
	// construct physics body if necessary
	if (!entity_body) {
		
		// create collision shape
		if (model() && model()->radius()) {
			const float modelscale = radius() / model()->radius();
			
			if (model()->collisionmodel() && model()->collisionmodel()->size()) {
				// complex collision is enabled and a valid collision model has been found
				btCompoundShape *compoundshape = new btCompoundShape();
				for (model::CollisionModel::CollisionMeshes::iterator it = model()->collisionmodel()->meshes().begin();
					it != model()->collisionmodel()->meshes().end(); it++) {
					
					model::CollisionMesh *mesh = (*it);
					
					// generate a btCollisionShape for each model::CollisionMesh in the model::CollisionModel
					// dynamic entities use btGImpactMeshShape
					btGImpactMeshShape *meshshape = new btGImpactMeshShape(mesh->triangles());
					meshshape->setMargin(Cvar::sv_collisionmargin->value());
				
					const float meshscale = mesh->scale() * modelscale;
					const btVector3 meshscalevec(meshscale, meshscale, meshscale);
					meshshape->setLocalScaling(meshscalevec);
					meshshape->updateBound();

					// apply collision mesh properties to the btCollisionShape 
					btTransform child_transform;
					child_transform.setIdentity();
					child_transform.setOrigin(to_btVector3(mesh->location() * modelscale));
					child_transform.setBasis(to_btMatrix3x3(mesh->axis()));
					
					switch (mesh->type()) {
						case model::FragmentGroup::Rotate:
						case model::FragmentGroup::Move:
							meshshape->setUserPointer((void *) mesh);
							break;
							
						default:
							meshshape->setUserPointer(0);
					}
					
					// add the mesh to the compoundhape and store a pointer to the child shape
					compoundshape->addChildShape(child_transform, meshshape);
					entity_collision_child_shapes.push_back(meshshape);
				}
			
				entity_collision_shape = compoundshape;
				
			} else {
				// use bounding box
				btBoxShape *boxshape =  new btBoxShape(to_btVector3(model()->box().max() * modelscale));
				entity_collision_shape = boxshape;
			}
			
		} else {
			// use a sphere with a radius matching the entity
			btSphereShape *sphereshape = new btSphereShape(radius());
			entity_collision_shape = sphereshape;
		}
		
		// set margin
		entity_collision_shape->setMargin(Cvar::sv_collisionmargin->value());

		// calculate inertia
		btVector3 inertia(0, 0, 0);
		if (entity_mass)
			entity_collision_shape->calculateLocalInertia(entity_mass, inertia);
		
		// create motion state
		entity_motionstate = new btDefaultMotionState(t);
		
		// create physics body
		entity_body_info = new btRigidBody::btRigidBodyConstructionInfo(entity_mass, entity_motionstate, entity_collision_shape, inertia);	
		entity_body = new btRigidBody(*entity_body_info);
		// point the bullet user pointer to the entity
		entity_body->setUserPointer((void *) this);
		// enable custom collision callback
		entity_body->setCollisionFlags(entity_body->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK); 
		
		if (entity_mass) {
			entity_body->setActivationState(DISABLE_DEACTIVATION);
		} else {
			entity_body->setActivationState(ISLAND_SLEEPING);
		}

		if (zone())
			zone()->physics()->addRigidBody(entity_body);
	}

	// transfer entity location to motion state
	body()->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f));
	body()->setAngularVelocity(btVector3(0.0f, 0.0f, 0.0f));
	body()->setWorldTransform(t);
	body()->clearForces();
	
	if (motionstate()) {
		motionstate()->setWorldTransform(t);
	}

	/* Does not work
	 * Note: does not do what you'd think it does
	
	
	if (entity_mass) {
		if (entity_state == Docked) {
			entity_body->setActivationState(DISABLE_SIMULATION);
			entity_body->setCollisionFlags(entity_body->getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE);
		} else {
			entity_body->setCollisionFlags(entity_body->getCollisionFlags() & ~btCollisionObject::CF_NO_CONTACT_RESPONSE);
			entity_body->setActivationState(DISABLE_DEACTIVATION);
		}
	}
	*/
	
	set_dirty();
}

void EntityDynamic::frame(const unsigned long elapsed)
{
	Entity::frame(elapsed);
	
	if (entity_state == Docked) {
		return;
	}

	// transfer bullet state to entity state
	if (entity_body) {

		btTransform t;
		entity_motionstate->getWorldTransform(t);	
		get_location().assign(t.getOrigin());
		get_axis().assign(t.getBasis());
		
		// if speed goes to 0 in this frame, the entity still needs to be marked as dirty
		if (entity_speed > 0) {
			set_dirty();
		}
		
		if (entity_speed < SPEED_THRESHOLD) {
			entity_body->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f));
			entity_speed = 0.0f;
		} else {		
			entity_speed = (float) entity_body->getLinearVelocity().length();
		}
		
		if (entity_speed > 0) {
			set_dirty();
		}
		
		float entity_angular = (float) entity_body->getAngularVelocity().length();
		if (entity_angular > 0) {
			set_dirty();
		}
		if (entity_angular < SPEED_THRESHOLD) {
			entity_body->setAngularVelocity(btVector3(0.0f, 0.0f, 0.0f));
		}
	}
}

void EntityDynamic::collision(Entity *other)
{
}

void EntityDynamic::hit(Entity *other)
{
}

void EntityDynamic::serialize_server_create(std::ostream & os) const
{
	Entity::serialize_server_create(os);
	os << roundf(entity_speed * 100.0f) << " "
	<< entity_state << " ";

	if (entity_state != Normal) {
		os << entity_timer << " ";
	}
}

void EntityDynamic::receive_server_create(std::istream &is)
{
	Entity::receive_server_create(is);
	is >> entity_speed;
	entity_speed /= 100.0f;
	is >> entity_state;

	if (entity_state != Normal) {
		is >> entity_timer;
	} else {
		entity_timer = 0;
	}
}

void EntityDynamic::serialize_client_update(std::ostream & os) const
{
}

void EntityDynamic::receive_client_update(std::istream &is)
{
}

void EntityDynamic::serialize_server_update(std::ostream & os) const
{
	os << (visible() ? 1 : 0) << " ";
	if (visible()) {
		os << std::setprecision(8)
		<< location() << " "
		<< axis().forward() << " "
		<< axis().left() << " "
		<< roundf(entity_speed * 100.0f) << " "
		<< entity_state << " ";

		if (entity_state != Normal) {
			os << entity_timer << " ";
		}
	}
}

void EntityDynamic::receive_server_update(std::istream &is)
{
	unsigned int o = 0;
	is >> o; // visibility
	if (o) {
		set_visible();
		is >> get_location();
		// axis up vector is the crossproduct of forward and left
		is >> get_axis()[0];
		is >> get_axis()[1];
		get_axis()[2] = math::crossproduct(axis().forward(), axis().left());
		is >> entity_speed;
		entity_speed /= 100.0f;
		is >> entity_state;

		if (entity_state != Normal) {
			is >> entity_timer;
		} else {
			entity_timer = 0;
		}
	} else {
		set_visible(false);
	}
}

/*----- EntityControlable::ActionInterface ------------------------- */

EntityControlable::ActionInterface::ActionInterface(EntityControlable *entity)
{
	actioninterface_entity = entity;
}

EntityControlable::ActionInterface::~ActionInterface()
{
	actioninterface_entity = 0;
}
 
void EntityControlable::ActionInterface::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep)
{
	actioninterface_entity->action(deltaTimeStep);
}

void EntityControlable::ActionInterface::debugDraw(btIDebugDraw* debugDrawer)
{
}

/*----- EntityControlable ------------------------------------------ */

// server-side constructor
EntityControlable::EntityControlable() : EntityDynamic()
{
	entity_thrust = 0;

	target_direction = 0.0f;
	target_thrust = 0.0f;
	target_pitch = 0.0f;
	target_roll = 0.0f;
	target_strafe = 0.0f;
	target_afterburner = 0.0f;
	target_controlflags = 0;

	entity_owner = 0;
	
	entity_actioninterface = 0;
	entity_health = 100.0f;
}

// client-side constructor
EntityControlable::EntityControlable(std::istream & is) :
		EntityDynamic(is)
{
	entity_thrust = 0;

	target_direction = 0.0f;
	target_thrust = 0.0f;
	target_pitch = 0.0f;
	target_roll = 0.0f;
	target_strafe = 0.0f;
	target_afterburner = 0.0f;
	target_controlflags = 0;

	entity_owner = 0;
	
	entity_actioninterface = 0;
	entity_health = 100.0f;
}


EntityControlable::~EntityControlable()
{
	if (entity_owner)
		entity_owner->remove_asset(this);
	
	if (entity_actioninterface) {
		entity_zone->physics()->removeAction(entity_actioninterface);
		delete entity_actioninterface;
	}
}

void EntityControlable::set_owner(Player *owner)
{
	if (entity_owner)
		entity_owner->remove_asset(this);

	entity_owner = owner;

	if (entity_owner)
		entity_owner->add_asset(this);

}

void EntityControlable::set_thrust(float thrust)
{
	if (entity_thrust != thrust) {
		entity_thrust = thrust;
		set_dirty();
	}
}

void EntityControlable::serialize_server_create(std::ostream & os) const
{
	EntityDynamic::serialize_server_create(os);
	os << roundf(entity_thrust*100.0f) << " ";
	os << (entity_owner ? entity_owner->id() : 0) << " ";
	os << roundf(health()) << " ";
}

void EntityControlable::receive_server_create(std::istream &is)
{
	EntityDynamic::receive_server_create(is);

	is >> entity_thrust;
	entity_thrust /= 100.0f;

	entity_owner = 0;

	int owner_id = 0;
	is >> owner_id;
	if (owner_id && !owner()) {
		for (GameInterface::Players::iterator pit = game()->players().begin();!owner() && (pit != game()->players().end()); pit++) {
			Player *player = (*pit);
			if (player->id() == owner_id) {
				player->add_asset(this);
				// entity_owner = player; // add_asset already sets this
			}
		}
		if (!entity_owner) {
			con_warn << "could not find owner " << owner_id << " for entity " << id() << "\n";
		}
	}
	
	is >> entity_health;
}

void EntityControlable::serialize_client_update(std::ostream & os) const
{
	EntityDynamic::serialize_client_update(os);
	os << target_direction << " ";
	os << target_pitch << " ";
	os << target_thrust << " ";
	os << target_roll << " ";
	os << target_strafe << " ";
	os << target_vstrafe << " ";
	os << target_afterburner << " ";
	os << target_controlflags << " ";
	os << target_aim << " ";
}

void EntityControlable::receive_client_update(std::istream &is)
{
	EntityDynamic::receive_client_update(is);
	is >> target_direction;
	is >> target_pitch;
	is >> target_thrust;
	is >> target_roll;
	is >> target_strafe;
	is >> target_vstrafe;
	is >> target_afterburner;
	is >> target_controlflags;
	is >> target_aim;
}

void EntityControlable::serialize_server_update(std::ostream & os) const
{
	EntityDynamic::serialize_server_update(os);
	os << roundf(thrust() * 100.0f) << " ";
	os << roundf(health()) << " ";
}

void EntityControlable::receive_server_update(std::istream &is)
{
	EntityDynamic::receive_server_update(is);
	is >> entity_thrust;
	entity_thrust /= 100.0f;
	is >> entity_health;
}

void EntityControlable::set_target_thrust(float thrust)
{
	if (thrust != target_thrust) {
		target_thrust = thrust;
		set_dirty();
	}
}

void EntityControlable::set_target_direction(float direction)
{
	if (target_direction != direction) {
		target_direction = direction;
		set_dirty();
	}
}

void EntityControlable::set_target_pitch(float pitch)
{
	if (target_pitch != pitch) {
		target_pitch = pitch;
		set_dirty();
	}
}

void EntityControlable::set_target_roll(float roll)
{
	if (target_roll != roll) {
		target_roll = roll;
		set_dirty();
	}
}

void EntityControlable::set_target_strafe(float strafe)
{
	if (target_strafe != strafe) {
		target_strafe = strafe;
		set_dirty();
	}
}

void EntityControlable::set_target_vstrafe(float vstrafe)
{
	if (target_vstrafe != vstrafe) {
		target_vstrafe = vstrafe;
		set_dirty();
	}
}

void EntityControlable::set_target_aim(const math::Vector3f &aim)
{
	if (aim != target_aim) {
		target_aim.assign(aim);
		set_dirty();
	}
}

void EntityControlable::set_target_afterburner(float afterburner)
{
	if (target_afterburner != afterburner) {
		target_afterburner = afterburner;
		set_dirty();
	}
}

void EntityControlable::set_target_controlflags(int controlflags)
{
	if (target_controlflags != controlflags) {
		target_controlflags = controlflags;
		set_dirty();
	}
}

void EntityControlable::set_target_controlflag(const ControlFlags controlflag)
{
	if ((target_controlflags & controlflag) == 0) {
		target_controlflags = target_controlflags | controlflag;
		set_dirty();
	}
}

void EntityControlable::unset_target_controlflag(const ControlFlags controlflag)
{
	if ((target_controlflags & controlflag) == controlflag) {
		target_controlflags = target_controlflags & ~controlflag;
		set_dirty();
	}
}

void EntityControlable::set_zone(Zone *zone)
{
	if (entity_zone == zone)
		return;

	if (entity_zone) {
		entity_zone->remove(this);
		if (body() && entity_zone->physics()) {
			entity_zone->physics()->removeAction(entity_actioninterface);
			entity_zone->physics()->removeRigidBody(body());
		}
	}

	// oldzone is cleared after every game frame
	if (!entity_oldzone)
		entity_oldzone = entity_zone;

	entity_zone = zone;
	set_dirty();

	if (entity_zone) {
		entity_zone->add(this);		
		if (body() && entity_zone->physics()) {
			entity_zone->physics()->addRigidBody(body());
			entity_zone->physics()->addAction(entity_actioninterface);
			reset();
		}
	}
}

void EntityControlable::reset()
{
	// no bullet physics on NonSolid entities
	if (!radius() || has_flag(NonSolid)) {
		return;
	}

	// remove Docked and Destroyed entities from the physics simulation
	if (died() || (state() == core::Entity::Docked) || (state() == core::Entity::Destroyed)) {
		
		if (entity_body) {
		
			if (entity_motionstate) {
				delete entity_motionstate;
				entity_motionstate = 0;
			}
				
			if (zone() && zone()->physics()) {
				if (entity_actioninterface) {
					entity_zone->physics()->removeAction(entity_actioninterface);
					delete entity_actioninterface;
					entity_actioninterface = 0;
				}	
				entity_zone->physics()->removeRigidBody(body());
			}
			
			if (entity_collision_shape) {
				delete entity_collision_shape;
				entity_collision_shape = 0;
			}
			
			for (CollisionShapes::iterator sit = entity_collision_child_shapes.begin(); sit != entity_collision_child_shapes.end(); sit++) {
				delete (*sit);
				(*sit) = 0;
			}
			entity_collision_child_shapes.clear();
			
			if (entity_body) {
				delete entity_body;
				entity_body = 0;
			}
			
			if (entity_body_info) { 
				delete entity_body_info;
				entity_body_info = 0;
			}
		}
		
		return;
	}
	
	// location and orientation
	btTransform t;
	t.setIdentity();
	t.setOrigin(to_btVector3(location()));
	t.setBasis(to_btMatrix3x3(axis()));
	
	// construct physics body if necessary
	if (!entity_body) {
	
		// create collision shape
		if (model() && model()->radius()) {
			const float modelscale = radius() / model()->radius();
			
			if (model()->collisionmodel() && model()->collisionmodel()->size()) {
			// a valid collision model has been found
				btCompoundShape *compoundshape = new btCompoundShape();
				for (model::CollisionModel::CollisionMeshes::iterator it = model()->collisionmodel()->meshes().begin();
					it != model()->collisionmodel()->meshes().end(); it++) {
					
					model::CollisionMesh *mesh = (*it);
					
					// generate a btCollisionShape for each model::CollisionMesh in the model::CollisionModel
					// dynamic entities use btGImpactMeshShape
					btGImpactMeshShape *meshshape = new btGImpactMeshShape(mesh->triangles());
					meshshape->setMargin(Cvar::sv_collisionmargin->value());
					
					const float meshscale = mesh->scale() * modelscale;
					const btVector3 meshscalevec(meshscale, meshscale, meshscale);				
					meshshape->setLocalScaling(meshscalevec);
					meshshape->updateBound();

					// apply collision mesh properties to the btCollisionShape 
					btTransform child_transform;
					child_transform.setIdentity();
					child_transform.setOrigin(to_btVector3(mesh->location() * modelscale));
					child_transform.setBasis(to_btMatrix3x3(mesh->axis()));
					
					switch (mesh->type()) {
						case model::FragmentGroup::Rotate:
						case model::FragmentGroup::Move:
							meshshape->setUserPointer((void *) mesh);
							break;
							
						default:
							meshshape->setUserPointer(0);
					}
					
					// add the mesh to the compoundhape and store a pointer to the child shape
					compoundshape->addChildShape(child_transform, meshshape);
					entity_collision_child_shapes.push_back(meshshape);
				}
				
				entity_collision_shape = compoundshape;
				
			} else {
				// use bounding box
				btBoxShape *boxshape =  new btBoxShape(to_btVector3(model()->box().max() * modelscale));
				entity_collision_shape = boxshape;
			}
			
		} else {
			// use a sphere with a radius matching the entity
			btSphereShape *sphereshape = new btSphereShape(radius());
			entity_collision_shape = sphereshape;			
		}
		
		// set margin on each mesh
		entity_collision_shape->setMargin(Cvar::sv_collisionmargin->value());

		btVector3 inertia(0, 0, 0);
		if (entity_mass)
			entity_collision_shape->calculateLocalInertia(entity_mass, inertia);
				
		// create motion state
		entity_motionstate = new btDefaultMotionState(t);
		
		// create physics body
		entity_body_info = new btRigidBody::btRigidBodyConstructionInfo(entity_mass, entity_motionstate, entity_collision_shape, inertia);		
		entity_body = new btRigidBody(*entity_body_info);
		entity_body->setActivationState(DISABLE_DEACTIVATION);
		// point the bullet user pointer to the entity
		entity_body->setUserPointer((void *) this);
		// enable custom collision callback
		entity_body->setCollisionFlags(entity_body->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK); 
		
		//entity_body->setCollisionFlags(entity_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT); 
		
		entity_actioninterface = new ActionInterface(this);
		
		if (zone()) {
			entity_zone->physics()->addRigidBody(body());
			entity_zone->physics()->addAction(entity_actioninterface);
		}
	}

	body()->clearForces();
	body()->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f));
	body()->setAngularVelocity(btVector3(0.0f, 0.0f, 0.0f));
	body()->setWorldTransform(t);
	motionstate()->setWorldTransform(t);

	/* Does not work
	 
	if (entity_state == Docked) {
		entity_body->setActivationState(DISABLE_SIMULATION);
		entity_body->setCollisionFlags(entity_body->getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE);
	} else {
		entity_body->setActivationState(DISABLE_DEACTIVATION);
		entity_body->setCollisionFlags(entity_body->getCollisionFlags() & ~btCollisionObject::CF_NO_CONTACT_RESPONSE);
	}
	*/
	set_dirty();
}

// bullet physics frame (runs at bullet framerate)
void EntityControlable::action(btScalar seconds)
{
}

} //namespace core