#include "ModelBase.h"

#include "PhysicsBase.h"
#include "EntityBase.h"
#include "EntityFactoryBase.h"

#include <coll2d.h>

#include <assert.h>

namespace Engine {

static ModelBase* ModelInstance = NULL;

/*
 * Inherited Module functions
 */
int ModelBase::OnInit (int argc, char* argv[]) {
	LogDebug ("Model Init");

	ModelInstance = this;
	mKilledEntities.clear ();
	mEntityIdCounter = 0;
	mDeltaSec = 0.;
	mPlayerEntityId = NullEntityId;

	return 0;
}

void ModelBase::OnDestroy () {
	LogDebug ("Model Destroy");

	std::map<unsigned int, EntityBase*>::iterator iter = mEntities.begin ();
	std::map<unsigned int, EntityBase*>::iterator next = iter;
	
	// Since DestroyEntity () also erases the entry in mEntities iter gets
	// invalid. Therefore we have to store the iterator that follows iter
	while (iter != mEntities.end ()) {
		next = iter;
		next ++;
		DestroyEntity (iter->first);
		iter = next;
	}

	ModelInstance = NULL;
}

/*
 * Model specific functions
 */
void ModelBase::Process () {
	// Process the controllers and state of all entities
	EntityIter entity_iter = mEntities.begin();
	do {
		if (entity_iter->second == NULL) {
			LogError ("Entity with id %d does not exist!", entity_iter->first);
		}
		entity_iter->second->Update(mDeltaSec);
		entity_iter++;
	} while (entity_iter != mEntities.end());

	/*
	// Update the timers
	for (TimerIter timer_iter = mTimers.begin(); timer_iter != mTimers.end(); timer_iter++) {
		timer_iter->second.Update(mDeltaSec);
		timer_iter++;
	}
	*/

	// simulate the world
	mPhysics->Simulate (mDeltaSec, this);

	assert (mKilledEntities.size() <= mEntities.size());

	// remove killed entities
	if (mKilledEntities.size() > 0) {
		unsigned int i;
		LogDebug ("There are %d entities to kill", mKilledEntities.size());
		for (i = 0; i < mKilledEntities.size(); i++)
			UnregisterEntity (mKilledEntities[i]);

		mKilledEntities.clear();
	}
}

EntityBase* ModelBase::CreateEntity (int type) {
	// Create a new entity id
	unsigned int entity_id = CreateEntityId();

	// Call the event
	OnCreateEntity (type, entity_id);
	
	// Create the entity
	EntityBase* result = mEntityFactory->CreateEntity (type);
	result->SetId (entity_id);

	// And register it
	RegisterEntity (result);

	return result;
}

void ModelBase::RegisterEntity (EntityBase* entity) {
	unsigned int id = entity->mId;
	LogDebug ("Registering Entity with id '%u' and type '%u'", id, entity->mType);

	if (mEntities.find(id) != mEntities.end ()) {
		LogError ("Replacing Entity with id '%u' and type '%u'", id, entity->mType);
	}

	if (entity->mPhysicState)
		mPhysics->RegisterEntity (entity->mPhysicState);

	mEntities[id] = entity;
}

void ModelBase::KillEntity (const unsigned int id) {
	std::map<unsigned int, EntityBase*>::iterator iter = mEntities.find (id);

	LogDebug ("Killing entity with id '%u'", id);

	if (iter == mEntities.end ()) {
		LogError ("Could not kill Entity with id '%u': Entity not found!", id);
		return;
	} else {
		EntityBase *entity = iter->second;

		LogDebug ("Entity pointer = 0x%u", entity);
		// call the event handler
		OnKillEntity (entity);
		LogDebug ("Entity pointer (after kill) = 0x%u", entity);

		if (entity->mPhysicState) {
			entity->mPhysicState->mAlive = false;
		}

		mKilledEntities.push_back (iter->first);
	}
}

void ModelBase::UnregisterEntity (const unsigned int id) {
	std::map<unsigned int, EntityBase*>::iterator iter = mEntities.find (id);

	if (iter == mEntities.end ()) {
		LogDebug ("iter id=%u entitypointer=0x%x type=%d", iter->first, iter->second, iter->second->mType);
		LogError ("Could not unregister Entity with id '%d': Entity not found!", id);
		return;
	} else {
		EntityBase *entity = iter->second;
		if (entity->mPhysicState) {
			mPhysics->UnregisterEntity (id);
			entity->mPhysicState = NULL;
		}

		if (entity->mControllerState) {
			delete entity->mControllerState;
			entity->mControllerState = NULL;
		}

		delete entity;

		mEntities.erase (iter);
	}
}

EntityBase* ModelBase::GetEntity (const unsigned int id) {
	if (id == NullEntityId)
		return NULL;

	std::map<unsigned int, EntityBase*>::iterator iter = mEntities.find (id);

	if (iter != mEntities.end ()) {
		return iter->second;
	}

	return NULL;
}

EntityBase* ModelBase::GetEntityAt (const vector3d &pos) {
	std::map<unsigned int, EntityBase*>::iterator iter = mEntities.begin();

	while (iter != mEntities.end ()) {
		EntityPhysicState* entity = iter->second->mPhysicState;
		/// \todo fix const casting!
		vector3d temp_pos (pos);

		if ( (temp_pos - entity->GetPosition()).length() < entity->mRadius) 
			return iter->second;

		iter++;
	}

	return NULL;
}

unsigned int ModelBase::CreateEntityId () {
	if (mEntityIdCounter == NullEntityId - 1)
		LogError ("Could not create valid entity id, reached maximum value of %u", mEntityIdCounter);

	return ++mEntityIdCounter;
}

void ModelBase::ClearEntities () {
	LogDebug ("Clearing all %d entities.", mEntities.size());

	std::map<unsigned int, EntityBase*>::iterator iter = mEntities.begin ();
	std::map<unsigned int, EntityBase*>::iterator next = iter;
	
	// Since DestroyEntity () also erases the entry in mEntities iter gets
	// invalid. Therefore we have to store the iterator that follows iter
	while (iter != mEntities.end ()) {
		next = iter;
		next ++;
		DestroyEntity (iter->first);
		iter = next;
	}

	mEntityIdCounter = 0;

	// we alsohave to clear the vector of killed entities!
	mKilledEntities.clear();
}

unsigned int ModelBase::GetPlayerEntityId () {
	return mPlayerEntityId;
}

void ModelBase::SetPlayerEntityId(unsigned int entity_id) {
	mPlayerEntityId = entity_id;
}

/**
 * \param collision_time The time when the collision occured relative to the start of the simulation frame 
 */
void ModelBase::SendEntityCollisionEvent (const unsigned int reference_entity_id,
		const unsigned int incidence_entity_id, float collision_time, vector3d point, vector3d normal) {
	// Check whether we report the same contact over and over
	static unsigned int last_reference_entity_id = 0;
	static unsigned int last_incidence_entity_id = 0;
	static float last_collision_time = -1.;

	if (!reference_entity_id || !incidence_entity_id)
		return;

	if (last_reference_entity_id != 0) {
		if (last_reference_entity_id == reference_entity_id
				&& last_incidence_entity_id == incidence_entity_id
				&& last_collision_time == collision_time) {
			return;
		}
	}

	// update the last_* entries
	last_reference_entity_id = reference_entity_id;
	last_incidence_entity_id = incidence_entity_id;
	last_collision_time = collision_time;

	// now we need to get the Entities and check whether we should
	// call EntityBase::CollisionEvent()
	EntityBase* reference_entity = GetEntity (reference_entity_id);	
	EntityBase* incidence_entity = GetEntity (incidence_entity_id);	

	LogDebug ("Received collision type ref = %d type inc = %d",
			reference_entity->mType,
			incidence_entity->mType);

	// If the incidence entity accepted the collision we can return, otherwise
	// we perform the symmetric collision event.
	if (incidence_entity->CollisionEvent (reference_entity, point, normal))
		return;

	reference_entity->CollisionEvent (incidence_entity, point, normal * -1.);
}

/*
 * Global functions
 */
unsigned int GetPlayerEntityId () {
	if (!ModelInstance) {
		LogError ("Couldn't get Player id: Model not initialized!");
	}

	return ModelInstance->GetPlayerEntityId ();
}

void SetPlayerEntityId(unsigned int entity_id) {
	if (!ModelInstance) {
		LogError ("Couldn't set Player id: Model not initialized!");
	}

	ModelInstance->SetPlayerEntityId (entity_id);
}

float GetFrameDuration () {
	if (!ModelInstance) {
		LogError ("Couldn't get frame duration: Model not initialized!");
	}

	return ModelInstance->GetFrameDuration ();
}

float GetDurationApplicationStart () {
	if (!ModelInstance) {
		LogError ("Couldn't get application runtime: Model not initialized!");
	}

	return ModelInstance->GetDurationApplicationStart ();
}


EntityBase * CreateEntity (int type) {
	if (!ModelInstance) {
		LogError ("Couldn't create Entity: Model not initialized!");
	}

	EntityBase *result = ModelInstance->CreateEntity (type);

	return result;
}

void DestroyEntity (unsigned int id) {
	if (!ModelInstance) {
		LogError ("Couldn't destroy Entity: Model not initialized!");
	}

	ModelInstance->UnregisterEntity (id);
}

void KillEntity (unsigned int id) {
	if (!ModelInstance) {
		LogError ("Couldn't kill Entity: Model not initialized!");
	}

	ModelInstance->KillEntity (id);
}

EntityBase* GetEntity (unsigned int id) {
	if (!ModelInstance) {
		LogError ("Couldn't execute GetEntity(): Model not initialized!");
	}

	return ModelInstance->GetEntity (id);
}

EntityBase* GetEntityAt(const vector3d &pos) {
	if (!ModelInstance) {
		LogError ("Couldn't execute GetEntityAt(): Model not initialized!");
	}

	return ModelInstance->GetEntityAt (pos);
}

EntityPhysicState * GetEntityPhysicState (unsigned int id) {
	EntityBase *entity = GetEntity (id);

	if (entity)
		return entity->mPhysicState;

	return NULL;
}

/*
void StartTimer(const std::string &id, float sec) {
	if (!ModelInstance) {
		LogError ("Couldn't execute GetEntity(): Model not initialized!");
	}

	ModelInstance->StartTimer(id, sec);
}

bool CheckTimer(const std::string &id) {
	if (!ModelInstance) {
		LogError ("Couldn't execute GetEntity(): Model not initialized!");
	}
	
	return ModelInstance->CheckTimer(id);
}
*/

// /** \brief Starts a timer with the given id that expires after sec seconds */
void StartTimer(const std::string &id, float sec) {
	if (!ModelInstance) {
		LogError ("Couldn't query Timer: Model not initialized!");
	}

	ModelInstance->StartTimer(id, sec);
}
 
// /** \brief Checks whether a timer expired */
bool CheckTimer(const std::string &id) {
	if (!ModelInstance) {
		LogError ("Couldn't query Timer: Model not initialized!");
	}

	return ModelInstance->CheckTimer(id);
}

// /** \brief Checks whether a timer expired */
float GetTimer(const std::string &id) {
	if (!ModelInstance) {
		LogError ("Couldn't query Timer: Model not initialized!");
	}

	return ModelInstance->GetTimer(id);
}


}


