#include "ParticleManager.h"

#include <assert.h>


#include "MemoryManager/DebugNew.h"


using namespace ParticleSystem;
ParticleManager * ParticleManager::global = NULL;


ParticleManager::ParticleManager(void)
{
	particleEngines = new list<PEngine>;

}

ParticleManager::~ParticleManager(void)
{
	// on enleve l'ensemble des moteurs de particule de la liste (avec suppression de la memoire si besoin est.
	removeAll();
	delete particleEngines;
}

ParticleManager & ParticleManager::getGlobal()
{
	// on verifie si l'instance global est instanciée
	if (global == NULL)
	{
		// elle n'est pas instanciée, on la crée
		global = new ParticleManager();
	}
	// on retourne une reference sur le pointeur de l'instance globale.
	return *global;
}

void ParticleManager::destroyGlobal()
{
	// on detruit l'instance global (qui est a NULL si elle n'a pas été instanciée)
	delete global;
	// on met l'instance globale a null
	global = NULL;
}

ParticleManager::PEngine * ParticleManager::foundEngine(ParticleEngine * e)
{
	// on parcourt l'ensemble des moteurs de particules.
	for (list<ParticleManager::PEngine>::iterator it = particleEngines->begin(); it != particleEngines->end(); it++)
	{
		// on a trouvé le pointeur recherché.
		if (it->engine == e)
		{
			// mmmmmhhh pas mal ca... on retourne la référence du déréférencement de l'iterateur...
			return &(*it);
		}
	}
	// on a parcouru l'ensemble des moteurs de particules, on ne l'a pas trouvé donc
	// on retourne NULL
	return NULL;
}

void ParticleManager::addEngine(ParticleEngine * e,ParticleEngineMemoryManagment mode,bool end)
{
	// on ne prend pas en compte le mode NOT_EXISTING...
	assert(mode != NOT_EXISTING);
	// on verifie que le moteur de particules n'existe pas deja.
	assert(foundEngine(e) == NULL);
	// on crée un PEngine.
	ParticleManager::PEngine engine;
	// on lui donne le pointeur sur le moteur de particules.
	engine.engine = e;
	// et on definit le mode de gestion de memoire
	engine.mode = mode;
	// et on l'ajoute a la liste de moteurs de particules.
	if (end)
	{
		// l'utilisateur a demandé a ce que le moteur soit ajouté a la fin de la liste : on l'ajoute a la fin
		particleEngines->push_back(engine);
	}
	else
	{
		// l'utilisateur a demandé a ce que le moteur soit ajouté au debut de la liste : on l'ajoute au debut.
		particleEngines->push_front(engine);
	}
	// d'apres la doc, on doit redemmarer l'iteration. on le fait donc, cela previent les probleme lié a l'ajout lors
	// d'un parcours.
	startIteration();
}

void ParticleManager::removeEngine(ParticleEngine * e)
{
	// on parcourt toute la liste de moteurs de particules. on ne peut pas utiliser la methode foundEngine car
	// il nous faut un iterateur pour pouvoir enlever le moteur de particules de la liste.
	for (list<ParticleManager::PEngine>::iterator it = particleEngines->begin(); it != particleEngines->end(); it++)
	{
		// on a trouvé le moteur de particules passé en parametres.
		if (it->engine == e)
		{
			// il se "detruit", c'est a dire qu'il detruit le moteur de particules si il est en mode managed.
			it->destroy();
			// et on le suprime de la liste.
			it = particleEngines->erase(it);
			// d'apres le contrat de la classe, on doit redemarrer l'iteration pour assurer de ne pas avoir suprimmer
			// un element non encore parcouru.
			startIteration();
			return;
		}
	}
	// si on est arrivé la, c'est que l'utilisateur a essayé de supprimer un moteur de
	// particule qui n'etait pas dans la liste...
	assert(foundEngine(e) != NULL);
	// et si on est arrivé la, c'est qu'il y a vraiment un probleme encore plus important, le moteur de particule
	// existe bien dans la list (cf ligne au dessus) et que l'on ne l'a pas trouvé lors de l'iteration...
	// la on doit imperativement arreter le programme...
	assert(false);
}

void ParticleManager::removeAll()
{
	// on parcourt la liste et on detruit les moteurs de particules
	for (list<ParticleManager::PEngine>::iterator it = particleEngines->begin(); it != particleEngines->end(); it++)
	{
		// chaque moteur de particule se detruit en fonction de son mode de gestion memoire.
		it->destroy();
	}
	// et on vide la liste.
	particleEngines->clear();
}

void ParticleManager::update(float elapsedTime)
{
	// pour chaque moteur de particules
	for (list<ParticleManager::PEngine>::iterator it = particleEngines->begin(); it != particleEngines->end(); it++)
	{
		// on le met a jours.
		it->engine->update(elapsedTime);
		// si il est terminé et qu'il est en mode managed
		if (it->engine->isFinish())
		{
			switch (it->mode)
			{
			case MANAGED:
				it->destroy();
				it = particleEngines->erase(it);
				break;
			case REMOVABLE: // on pourrait aussi faire it->destroy mais ca ne sert a rien ici
				it = particleEngines->erase(it);
				break;
			case UNMANAGED:
			case NOT_EXISTING:
				// nothing to do...
				break;
			}
		}
	}
}

void ParticleManager::startIteration()
{
	// on demarre l'iteration
	iterator = particleEngines->begin();
}

ParticleEngine& ParticleManager::current()
{
	// on retourne la reference sur le moteur courante de l'iterateur.
	return *(iterator->engine);
}

void ParticleManager::next()
{
	// si l'iteration n'est pas terminée
	if (!iterationFinished())
	{
		// on passe au suivant
		iterator++;
	}
}

bool ParticleManager::iterationFinished()
{
	// on verifie si l'iteration est terminée.
	return (iterator == particleEngines->end());
}

ParticleEngineMemoryManagment ParticleManager::exist(ParticleEngine * e)
{
	// on essaie de recuperer le moteur de particules dans la liste
	ParticleManager::PEngine * pe = foundEngine(e);
	if (pe == NULL)
	{
		// il n'existe pas, on retourne NOT_EXISTING conformement à la documentation
		return NOT_EXISTING;
	}
	else
	{
		// il existe, on retourne le mode de gestion.
		return pe->mode;
	}
}

