/* 
 * Tactic is the legal property of its developers, whose names are too numerous 
 * to list here.  Please refer to the COPYRIGHT file distributed with this 
 * source distribution. 
 * 
 * This library is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation; either 
 * version 2.1 of the License, or (at your option) any later version. 
 * 
 * This library 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 
 * Lesser General Public License for more details. 
 * 
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this library; if not, write to the Free Software 
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 *
 * See README for more informations
 *
 * BasicAI.h
 *
 * This a the basic AI for an Army. First integration
 *
 * Date 09/04/06
 *
 */

#include "BasicAI.h"

#include "MemoryManager/debugNew.h"

using namespace ground;

BasicAI::BasicAI(std::string& color) : Army(color){
	this->ennemyManager = new EnnemyManager(this->groundArmyInfo);
	ie = InferenceEngine();
	nbMaxGroup = 3;
	nbUpdates = 1;
}

BasicAI::~BasicAI(void){
	std::vector<Unit*>::iterator it;
	for(it=this->dead.begin(); it!=this->dead.end();++it){
		delete (*it);
	}
	this->dead.clear();

	std::vector<GroupAI*>::iterator jt;
	for(jt=this->groupUnits.begin(); jt!=this->groupUnits.end();++jt){
		delete (*jt);
	}
	this->groupUnits.clear();

	delete ennemyManager;
}

void BasicAI::updateAI(){
	DebugAI::trace('e', "Entering Update AI");
	DebugAI::trace('c', "Color of the army is "+this->color);	 
	std::vector<GroupAI*> vect;

	int nbGroupUpdate = 1; // debug variable
	std::vector<GroupAI*>::iterator it = groupUnits.begin();
	while(it != groupUnits.end()){

		DebugAI::trace('e', "Update AI number "+Converter::floatToString((float)nbUpdates));
		DebugAI::trace('e', "Updating group number "+Converter::floatToString((float)nbGroupUpdate));
		DebugAI::trace('z', "Number of units of the group is "+Converter::floatToString((float)(*it)->getNumber()));

		// Number of Units in the current group
		int nbUnits = (*it)->getNumber();
		if(nbUnits == 0){
			DebugAI::trace('z', "Erasing group");	
			it = groupUnits.erase(it);
			if(it == groupUnits.end()) break;
			continue;
		}
		
		GroupEnnemy * closest = this->ennemyManager->getClosestGroup((*it)->getPosition());
		if(closest != NULL) (*it)->setGroupEnnemy(closest);
		if(nbUnits > 0) (*it)->update();

		// Forçage des états pour la division ou le regroupement
		switch((*it)->getState()->type){
			case FLEE : 
				// Si un groupe est en etat FLEE alors il va se joindre à un autre groupe
				if (groupUnits.size() + vect.size() >= 1) {
					GroupAI* gai;
					gai = closestGroup((*it)->getPosition(), (*it));

					if (gai != NULL && !gai->isObeying()) {
						DebugAI::trace('z', "Merge group");
						for (std::vector<Unit*>::iterator ut = (*it)->getUnits()->begin();
							ut != (*it)->getUnits()->end(); ++ut) {

							gai->addUnit(*ut);
							//(*it)->removeUnit(*ut);
						}

						gai->setObey(true);
						gai->setState(EVALUATION);
						gai->setOrder(ORDER_REGROUP);

						DebugAI::trace('z', "Erasing group");
						it = groupUnits.erase(it);
					}
				}
				break;
			// si un groupe est en reconnaissance on le divise			
			case MOVE : 
				if(nbUnits > 1 && groupUnits.size() + vect.size() <= nbMaxGroup){
					DebugAI::trace('z', "Dividing group");
					GroupAI* gai = new GroupAI(&ie, ennemyManager);
					gai->setInfoGround(&groundArmyInfo);
					gai->setGroupEnnemy(&emptyGroupEnnemies);
					for(int i = 0; i < nbUnits/2; i++){
						gai->addUnit((*it)->getUnits()->at(0));
						(*it)->removeUnit((*it)->getUnits()->at(0));
					}
					(*it)->setOrder(ORDER_REGROUP);
					vect.push_back(gai);
				}		
				break;	

		}

		if(it == groupUnits.end()) break;

		++it;
		nbGroupUpdate++; // debug purpose

	} // for (std::vector<GroupAI*>::iterator it = groupUnits.begin(); it != groupUnits.end(); ++it)

	for(int i = 0; i < vect.size(); i++) {
		DebugAI::trace('e', "Updating additionnal group number "+Converter::floatToString((float)i+1));
		groupUnits.push_back(vect.at(i));
		vect.at(i)->setOrder(ORDER_REGROUP);
		//vect.at(i)->resetActions();
		vect.at(i)->update();
	}
	vect.clear();

	// If we have only one group
	if(groupUnits.size() == 1){
		// The group don't have to flee, we force the attack state
		if(groupUnits.at(0)->getState()->type == FLEE){
		   groupUnits.at(0)->setState(FIGHT);
		   groupUnits.at(0)->setOrder(ORDER_ATTACK);
		}
	}

	nbUpdates++;
	DebugAI::trace('e', "Leaving Update AI");
}

void BasicAI::updateUnit(){	
	//printf("update ground army info\n");
	this->groundArmyInfo.update();
	//printf("update ennemy manager\n");
	ennemyManager->update();	
	//printf("hop1\n");
	std::vector<Unit*>::iterator it;
	for (std::vector<GroupAI*>::iterator it = groupUnits.begin(); it != groupUnits.end(); ++it) {
		(*it)->removeDeadUnits();
	}
	std::vector<Unit*>::iterator kt = units.begin();
	while(kt != units.end()){
		(*kt)->update();
		if ((*kt)->isDead()){
			dead.push_back(*kt);
			groundArmyInfo.removeUnit(*kt);
			kt = units.erase(kt);
			if (kt == units.end()) {
				break;
			}
		}
		++kt;
	}

	//printf("update ennemy manager DONE\n");
	
	//std::cout << this->color<<" DONE\n";
}

EnnemyManager* BasicAI::getEnnemyManager(){
	return this->ennemyManager;
}

void BasicAI::manageGroups(){
	GroupAI * group = new GroupAI(&ie, ennemyManager);
	group->setInfoGround(&groundArmyInfo);
	group->setGroupEnnemy(&emptyGroupEnnemies);
	// on ajoute toute les unité dans le groupAI
	for(std::vector<Unit*>::iterator it = units.begin(); it != units.end(); ++it){
		group->addUnit(*it);
	}
	groupUnits.push_back(group);
	//group->setGroupEnnemy(ennemyManager->getClosestGroup(group->getPosition()));
}

GroupAI* BasicAI::closestGroup(Point2D pt, GroupAI * groupAI){

	std::vector<GroupAI*>::iterator it;

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

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

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

}