/* 
 * 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
 *
 * OrderAttack.cpp
 *
 * ...
 * Date 22/03/06
 *
 */
#include "OrderAttack.h"
#include "GroupAI.h"

OrderAttack::OrderAttack() {}

OrderAttack::OrderAttack(GroupAI* g) : Order(g)
{}

void OrderAttack::execute()
{
	DebugAI::trace('o', "Executing OrderAttack");

	Order::execute();
	myGroup->resetActions();

	std::list<EnnemyUnit*> archers, lancers, infantry, mounted;
	GroupEnnemy* e = myGroup->getGroupToAttack();
	position = myGroup->getPosition();

	if (e != NULL) {

		for (std::vector<EnnemyUnit*>::iterator it = e->getUnits().begin(); it != e->getUnits().end(); ++it)
		{
			(*it)->updateDistance(position); // needed for sorting afterward	
			if ((*it)->isMounted())
				mounted.push_back(*it);
			else
			{
				switch ((*it)->getType())
				{
					case unitEnumeration::ARCHER:
						archers.push_back(*it);
						break;
					case unitEnumeration::LANCER:
						lancers.push_back(*it);
						break;
					case unitEnumeration::INFANTRY:
						infantry.push_back(*it);
						break;
				} // switch
			} // if else
		} // for

		/**
		 * Sorting ennemies from closest to farest
		 */
		archers.sort(&OrderAttack::cmpEnnemies);
		lancers.sort(&OrderAttack::cmpEnnemies);
		infantry.sort(&OrderAttack::cmpEnnemies);
		mounted.sort(&OrderAttack::cmpEnnemies);

		std::vector<EnnemyUnit*> ennemies;
		ennemies.reserve(e->getUnits().size());
		for (std::vector<Unit*>::iterator it = myGroup->getUnits()->begin(); it != myGroup->getUnits()->end(); ++it)
		{
			(*it)->notifyEndOrder("OrderAttack");

			if ((*it)->isMounted())
				attack (&archers, &infantry, &mounted, &lancers, &ennemies);
			else
			{
				switch ((*it)->getType())
				{
					case unitEnumeration::ARCHER:
						attack (&infantry, &lancers, &mounted, &archers, &ennemies);
						break;
					case unitEnumeration::INFANTRY:
						attack (&lancers, &infantry, &mounted, &archers, &ennemies);
						break;
					case unitEnumeration::LANCER:
						attack (&mounted, &lancers, &archers, &infantry, &ennemies);
						break;
				} // switch
			} // else

			for (std::vector<EnnemyUnit*>::reverse_iterator jt = ennemies.rbegin(); jt != ennemies.rend(); ++jt) {
				(*it)->attack(**jt);
			} // for jt
		} // for it
	
	} // if (e != NULL)

	DebugAI::trace('o', "OrderAttack Executed");
}

OrderAttack::~OrderAttack() {}

FuzzyValueName OrderAttack::getType()
{
	return ORDER_ATTACK;
}

void OrderAttack::attack(std::list<EnnemyUnit*>*first, std::list<EnnemyUnit*>*second,
			std::list<EnnemyUnit*>*third, std::list<EnnemyUnit*>*forth,
			std::vector<EnnemyUnit*>* ennemies)
{
	if (!first->empty()) {

		std::list<EnnemyUnit*>::iterator unit = first->begin();
		EnnemyUnit * tempUnit = *unit;
		ennemies->push_back(tempUnit);
		first->erase(unit);
		first->push_back(tempUnit);

		std::list<EnnemyUnit*>::iterator it = first->begin();
		for (int i = 0; i < first->size()-1; i++) {
			ennemies->push_back(*it);
			++it;
		} // for
	} // if

	for(std::list<EnnemyUnit*>::iterator it = second->begin(); it != second->end(); ++it) {
		ennemies->push_back(*it);
	}

	for(std::list<EnnemyUnit*>::iterator it = third->begin(); it != third->end(); ++it) {
		ennemies->push_back(*it);
	}

	for(std::list<EnnemyUnit*>::iterator it = forth->begin(); it != forth->end(); ++it) {
		ennemies->push_back(*it);
	}

}


bool OrderAttack::cmpEnnemies (EnnemyUnit*& e, EnnemyUnit*& f)
{
	return (*e).getDistance() < (*f).getDistance();
}
