

#include "EnnemyManager.h"

#include "MemoryManager/debugNew.h"
#include "DebugAI.h"

EnnemyManager::EnnemyManager(ground::GroundArmyInfo& gai) : groundArmyInfo(gai)
{
	this->groups.clear();
}

EnnemyManager::~EnnemyManager(void)
{
	for(std::vector<GroupEnnemy*>::iterator it = groups.begin(); it != groups.end(); ++it){
		delete *it;
	}
	this->groups.clear();
	for(std::vector<EnnemyUnit*>::iterator it = allAliveEnnemies.begin(); it != allAliveEnnemies.end(); ++it){
		delete *it;
	}
	allAliveEnnemies.clear();
	for(std::vector<EnnemyUnit*>::iterator it = deadEnnemies.getUnits().begin(); it != deadEnnemies.getUnits().end(); ++it){
		delete *it;
	}
}


std::vector<GroupEnnemy*> EnnemyManager::getCloseGroups(Point2D& pt, float radius)
{
	std::vector<GroupEnnemy*> ret;
	std::vector<GroupEnnemy*>::iterator it;
	float dist;

	for(it=groups.begin(); it!=groups.end(); ++it) {
		dist = (pt-(*it)->getPosition()).getLength();

		if(dist<((*it)->getRadius()+radius)) {//on a trouvé un groupe plus près
			ret.push_back((*it));
		}
	}

	return ret;	//retour par copie

}

GroupEnnemy* EnnemyManager::getClosestGroup(Point2D& pt)
{
	std::vector<GroupEnnemy*>::iterator it;

	float current = 999999.0f;	//rayon infini...
	GroupEnnemy* ret = NULL;
	float tmp;

	for(it=groups.begin(); it!=groups.end(); ++it) {
		tmp = (pt-(*it)->getPosition()).getLength();

		if(tmp<current) {//on a trouvé un groupe plus près
			current = tmp;
			ret = (*it);
		}
	}
	return ret;
}

void EnnemyManager::update()
{
	DebugAI::trace('m', "\nENNEMY MANAGER UPDATE \n");				
	char buffer[255];
	// on sort les morts du traitement
	std::vector<EnnemyUnit*>::iterator it = allAliveEnnemies.begin();
	while(it != allAliveEnnemies.end()){
		// on test chaque unité vivante
		if (!(*it)->isAlive()){
			// elle est morte, on la met dans le vecteur d'ennemis morts
			deadEnnemies.addEnnemy(*it);
			
			(*it)->setState(EnnemyUnit::DEAD);
			// et on la sort du vecteur de vivants
			// puis on l'enleve du groupe potentiel dans lequel elle etait
			GroupEnnemy * ennemyGroup;		
			ennemyGroup = getGroupByUnit(*it);
			if (ennemyGroup != NULL){				
				ennemyGroup->removeEnnemy(*it);
			}
				
			
			hiddenEnnemies.removeEnnemy(*it);
			it = allAliveEnnemies.erase(it);
			if (it == allAliveEnnemies.end()){
				break;
			}
		}else{
			// l'unite est vivante
			if(groundArmyInfo.isVisible((*it)->getPosition())){
				// elle est visible...
				(*it)->setState(EnnemyUnit::VISIBLE);				
				(*it)->hiddenElapsedTime = 0;	
				
				sprintf(buffer," %x RAZ hidden ennemy elapsed time %d",*it,(*it)->hiddenElapsedTime);
				DebugAI::trace('m', buffer);
				// si elle etait deja visible avant, on ne fait rien, par contre, si on vient de l'appercevoir
				// (elle etait donc caché avant), on doit l'ajouter dans un groupe d'ennemies
				if (hiddenEnnemies.containEnnemy((*it))){
					// elle etait cachée, on prend le groupe d'unité le plus proche
					GroupEnnemy * closestGroup = getClosestGroup((*it)->getPosition());
					// si il n'existe pas de groupe proche ou que le groupe le plus proche est à plus de 7 cases, on en crée un nouveau
					if (closestGroup == NULL || (closestGroup->getPosition() - (*it)->getPosition()).getLength() > 7){
						// pas de groupe ou pas de groupe suffisament proche, on en crée un nouveau
						GroupEnnemy * newGroup = new GroupEnnemy();
						newGroup->addEnnemy((*it));
						groups.push_back(newGroup);
					}else{
						// on ajoute l'ennemi au groupe le plus proche...
						closestGroup->addEnnemy(*it);
					}
					// on enleve l'unité du groupe des unité caché
					hiddenEnnemies.removeEnnemy(*it);
				}
			}else{
					
				// on met l'unité comme non visible
				if (!hiddenEnnemies.containEnnemy(*it)&&(*it)->hiddenElapsedTime>150){	
					(*it)->hiddenElapsedTime++;
					sprintf(buffer," %x GOTOHIDE hidden ennemy elapsed time %d",*it,(*it)->hiddenElapsedTime);
					DebugAI::trace('m', buffer);
					(*it)->setState(EnnemyUnit::HIDDEN);
					hiddenEnnemies.addEnnemy(*it);
					// et on la retire de son groupe si elle en a un
					GroupEnnemy * ennemyGroup;		
					ennemyGroup = getGroupByUnit(*it);
					if (ennemyGroup != NULL){
						ennemyGroup->removeEnnemy(*it);
					}
				}else
					if (!hiddenEnnemies.containEnnemy(*it)){	
						(*it)->hiddenElapsedTime++;
						sprintf(buffer," %x MAJ hidden ennemy elapsed time %d",*it,(*it)->hiddenElapsedTime);
						DebugAI::trace('m', buffer);
					}else{						
						sprintf(buffer," %x RIEN hidden ennemy elapsed time %d",*it,(*it)->hiddenElapsedTime);
						DebugAI::trace('m', buffer);
					}
			}
		}
		++it;
	}
	std::vector<GroupEnnemy*>::iterator jt=groups.begin();
	while(jt!=groups.end()){
		if ((*jt)->getNumber() == 0){
				delete (*jt);
				jt = groups.erase(jt);
		}
		if (jt == groups.end()){
			break;
		}
		++jt;
	}
}
//	//printf("mise a jour groupes\n");
//	for(std::vector<GroupEnnemy*>::iterator it=groups.begin(); it!=groups.end(); ++it) {
//		for(std::vector<EnnemyUnit*>::iterator jt = (*it)->getUnits().begin(); jt != (*it)->getUnits().end(); ++jt){
//			if(!(*jt)->isAlive()){
//				//std::cout << __LINE__ << __FILE__ << std::endl;
//				(*it)->removeEnnemy((*jt));
//				// on retire le mort du vecteur d'unités.
//				for(std::vector<EnnemyUnit*>::iterator kt = allAliveEnnemies.begin(); kt != allAliveEnnemies.end(); ++kt){
//					if (*kt == *jt){
//						allAliveEnnemies.erase(kt);
//						break;
//					}
//				}
//				//std::cout << __LINE__ << __FILE__ << std::endl;
//				deadEnnemies.addEnnemy((*jt));
//				//std::cout << __LINE__ << __FILE__ << std::endl;
//				groundArmyInfo.removeDangerGenerator((*jt));
//				//std::cout << __LINE__ << __FILE__ << std::endl;
//			}else if(!groundArmyInfo.isVisible((*jt)->getPosition())){
//				std::cout << __LINE__ << __FILE__ << std::endl;
//				(*it)->removeEnnemy((*jt));
//				hiddenEnnemies.addEnnemy((*jt));
//				//std::cout << __LINE__ << __FILE__ << std::endl;
//			} else {
//				//std::cout << __LINE__ << __FILE__ << std::endl;
//				// l'unite est visible
//				if (hiddenEnnemies.containEnnemy((*jt))){
//					//std::cout << __LINE__ << __FILE__ << std::endl;
//					hiddenEnnemies.removeEnnemy((*jt));
//					GroupEnnemy * closest = this->getClosestGroup((*jt)->getPosition());
//					if (closest == NULL || (closest->getPosition() - (*jt)->getPosition()).getLength() > 7){
//						// l'ennemi est trop loins d'un autre groupe, il faut en créer un nouveau
//						GroupEnnemy * newGroup = new GroupEnnemy();
//						newGroup->addEnnemy((*jt));
//						groups.push_back(newGroup);
//					}else{
//						// on ajoute l'ennemi au groupe le plus proche...
//						closest->addEnnemy(*jt);
//					}
//				}
//			}
//		}
//		// on supprime les groupes vides
//		if ((*it)->getNumber() == 0){
//			delete (*it);
//			it = groups.erase(it);
//		}
//	}
//}

int EnnemyManager::getVisible(){
	int ret = 0;
	if (allAliveEnnemies.size() == 0){
		return 0;
	}
	for(std::vector<EnnemyUnit*>::iterator it = allAliveEnnemies.begin(); it != allAliveEnnemies.end(); ++it){
		if (groundArmyInfo.isVisible((*it)->getPosition())){
			ret++;
		}
	}
	//for(std::vector<GroupEnnemy*>::iterator it=groups.begin(); it!=groups.end(); ++it) {
	//	for(std::vector<EnnemyUnit*>::iterator jt = (*it)->getUnits().begin(); jt != (*it)->getUnits().end(); ++jt){
	//		if(groundArmyInfo.isVisible((*jt)->getPosition())){
	//			ret++;
	//		}
	//	}
	//}

	return ret;
}

int EnnemyManager::getAlive(){
	/*int ret = 0;
	for(std::vector<GroupEnnemy*>::iterator it=groups.begin(); it!=groups.end(); ++it) {
		for(std::vector<EnnemyUnit*>::iterator jt = (*it)->getUnits().begin(); jt != (*it)->getUnits().end(); ++jt){
			if((*jt)->isAlive()){
				ret++;
			}
		}
	}*/
	return allAliveEnnemies.size();
}

void EnnemyManager::addEnnemy(EnnemyUnit* ennemy){
	allAliveEnnemies.push_back(ennemy);
	// on l'ajoute caché
	hiddenEnnemies.addEnnemy(ennemy);
}



GroupEnnemy* EnnemyManager::getGroupByUnit(EnnemyUnit* unit){
	GroupEnnemy* myGroup = NULL;
	for(std::vector<GroupEnnemy*>::iterator it=groups.begin(); it!=groups.end(); ++it) {
		if ((*it)->containEnnemy(unit)){
			myGroup = *it;
			return myGroup;
		}			
	}
	return myGroup;
}

EnnemyUnit* EnnemyManager::getEnnemyInRange (Point2D pt, float range, float move) {
	float currentDistance;
	EnnemyUnit* ennemy = NULL;
	float closestDistance = 9999.0f;

	// We are looking for the closest ennemy
	// If the ennemy we find is closer than range, we have found
	// If the ennemy is not within range+move, we don't consider it
	for(std::vector<EnnemyUnit*>::iterator it=allAliveEnnemies.begin(); it!=allAliveEnnemies.end(); ++it) {
		currentDistance = (pt - (*it)->getPosition()).getLength();
		if(groundArmyInfo.isVisible((*it)->getPosition())){
			if (ennemy == NULL) {
				ennemy = (*it);
				closestDistance = currentDistance;
			} else if ( (pt - (*it)->getPosition()).getLength() <= range + move
					&& currentDistance < closestDistance ) {
					ennemy = (*it);
					closestDistance = currentDistance;
			}
			if (closestDistance < range) return ennemy;
		}
	}

	return ennemy;
}
