;// part of the "Attack" files, which share some functions
// An "Attack the city" creates a full fledged army.
// Note the "targetFinder" and the type ought not to be used in case of a cityAttack
function CityAttack(gameState, HeadQuarters,uniqueID, targetEnemy, type , targetFinder){
	
	//This is the list of IDs of the units in the plan
	this.idList=[];
	
	this.previousTime = 0; // nor sure if used
	this.state = "unexecuted";
	this.targetPlayer = targetEnemy;
	if (this.targetPlayer === -1 || this.targetPlayer === undefined)
	{
		// let's find our prefered target, basically counting our enemies units.
		var enemyCount = {};
		for (var i = 1; i <=8; i++)
			enemyCount[i] = 0;
		gameState.getEntities().forEach(function(ent) { if (gameState.isEntityEnemy(ent) && ent.owner() !== 0) { enemyCount[ent.owner()]++; } });
		var max = 0;
		for (i in enemyCount)
			if (enemyCount[i] >= max)
			{
				this.targetPlayer = +i;
				max = enemyCount[i];
			}
	}
	debug ("target = " +this.targetPlayer);
	this.targetFinder = targetFinder || this.defaultTargetFinder;
	this.type = type || "normal";
	this.name = uniqueID;
	this.healthRecord = [];
	
	this.queue = new Queue();
	
	this.timeOfPlanStart = gameState.getTimeElapsed();	// we get the time at which we decided to start the attack
	this.maxPreparationTime = 360*1000;	// 6 minutes. It's likely not enough, though you never know, and anyway the AI will start another one on top
	this.SupportPlan = {};
	this.SupportPlanArray = [];
	
	this.onArrivalReaction = "proceedOnTargets";
	// no importance wether they are civ or champs... Actually the HeadQuarters will choose what to build from the list
	// so it depends on that one ( as I write this, it will build the unit it has the least amount of, so it's -very- dumb
	
	// Value O is the "current count", so at start it must be 0. Value 1 is "minimal required amount to start the plan"
	// Value 2 is "preferred amount"... If you set something like "0,0,4,100", the plan will build this kind of units but they won't be required
	// Siege can use this to great effectiveness.	Value 4 is "individual priority"
	// Apprently I have no set a maximum amount... Weird. TODO
	// putting "undefined" on one of the finer categories (archer…) means it's not important
	
	// note: this.categories MUST reflect the structure of the basic levels of "this.unitcount"
	this.categories = { "infantry" : {"melee" :"melee" ,"ranged" : "ranged"}, "cavalry" : { "melee" :"melee" ,"ranged" : "ranged" } , "siege" : 0 };
	
	this.unitCount = { "infantry" : { "ranged" : 0, "melee" : 0 }, "cavalry" : { "ranged" : 0, "melee" : 0 } };
	this.unitCount.cavalry.ranged = { "subCat" : ["archer","javelin"] , "usesSubcategories" : false, "archer" : undefined, "javelin" : undefined, "priority" : 1, "currentAmount" : 0, "minimalAmount" : 3, "preferedAmount" : 4 };
	this.unitCount.cavalry.melee = { "subCat" : undefined, "usesSubcategories" : false, "swordsman" : undefined, "spearman" : undefined, "priority" : 1,
		"currentAmount" : 0, "minimalAmount" : 3, "preferedAmount" : 4 };
	this.unitCount.infantry.ranged = { "subCat" : undefined, "usesSubcategories" : false, "archer" : undefined, "javelin" : undefined, "slinger" : undefined, "priority" : 1,	"currentAmount" : 0, "minimalAmount" : 5, "preferedAmount" : 8 };
	this.unitCount.infantry.melee = { "subCat" : undefined, "usesSubcategories" : false, "swordsman" : undefined, "spearman" : undefined,"pikeman" : undefined, "priority" : 1, "currentAmount" : 0, "minimalAmount" : 5, "preferedAmount" : 8 };
	this.unitCount.siege = { "subCat" : undefined, "usesSubcategories" : false, "priority" : 1,
		"currentAmount" : 0, "minimalAmount" : 0, "preferedAmount" : 2 };
	
	if (gameState.getTimeElapsed() > 900000)	// 15 minutes
	{
		this.unitCount.cavalry.ranged["minimalAmount"] = 5;
		this.unitCount.cavalry.melee["minimalAmount"] = 5;
		this.unitCount.infantry.ranged["minimalAmount"] = 10;
		this.unitCount.infantry.melee["minimalAmount"] = 10;
		this.unitCount.cavalry.ranged["preferedAmount"] = 10;
		this.unitCount.cavalry.melee["preferedAmount"] = 10;
		this.unitCount.infantry.ranged["preferedAmount"] = 20;
		this.unitCount.infantry.melee["preferedAmount"] = 20;
		this.unitCount.siege["preferedAmount"] = 5;
		this.unitCount.siege["minimalAmount"] = 2;
	} else {
		this.maxPreparationTime = 180000;
	}
	if (type === "harass_raid")
	{
		this.targetFinder = this.raidingTargetFinder;
		this.onArrivalReaction = "huntVillagers";
		
		this.type = "harass_raid";
		// This is a cavalry raid against villagers. A cavalry swordsman has a bonus against these. Only build these
		this.maxPreparationTime = 180000;	// 3 minutes.
		if (gameState.playerData.civ === "hele")	// hellenes have an ealry cavalry swordsman
		{
			this.unitCount.cavalry.melee = { "subCat" : ["swordsman"] , "usesSubcategories" : true, "swordsman" : undefined, "priority" : 1, "currentAmount" : 0, "minimalAmount" : 0, "preferedAmount" : 0 };
			this.unitCount.cavalry.melee.swordsman = { "priority" : 1, "currentAmount" : 0, "minimalAmount" : 4, "preferedAmount" : 7, "fallback" : "abort" };
		} else {
			this.unitCount.cavalry.melee = { "subCat" : undefined , "usesSubcategories" : false, "priority" : 1, "currentAmount" : 0, "minimalAmount" : 4, "preferedAmount" : 7 };
		}
		this.unitCount.cavalry.ranged["minimalAmount"] = 0;
		this.unitCount.cavalry.ranged["preferedAmount"] = 0;
		this.unitCount.infantry.ranged["minimalAmount"] = 0;
		this.unitCount.infantry.ranged["preferedAmount"] = 0;
		this.unitCount.infantry.melee["minimalAmount"] = 0;	
		this.unitCount.infantry.melee["preferedAmount"] = 0;
		this.unitCount.siege["preferedAmount"] = 0;
	}
	this.anyNotMinimal = true;	// used for support plans

	// taking this so that fortresses won't crash it for now. TODO: change the rally point if it becomes invalid
	if(gameState.ai.pathsToMe.length > 1)
		var position = [(gameState.ai.pathsToMe[0][0]+gameState.ai.pathsToMe[1][0])/2.0,(gameState.ai.pathsToMe[0][1]+gameState.ai.pathsToMe[1][1])/2.0];
	else
		var position = [gameState.ai.pathsToMe[0][0],gameState.ai.pathsToMe[0][1]];		
	var CCs = gameState.getOwnEntities().filter(Filters.byClass("CivCentre"));
	var nearestCCArray = CCs.filterNearest(position, 1).toEntityArray();
	var CCpos = nearestCCArray[0].position();
	this.rallyPoint = [0,0];
	this.rallyPoint[0] = (position[0]*3 + CCpos[0]) / 4.0;
	this.rallyPoint[1] = (position[1]*3 + CCpos[1]) / 4.0;
	if (type == 'harass_raid')
	{
		this.rallyPoint[0] = (position[0]*3.9 + 0.1 * CCpos[0]) / 4.0;
		this.rallyPoint[1] = (position[1]*3.9 + 0.1 * CCpos[1]) / 4.0;
	}
	
	// some variables for during the attack
	this.threatList = [];	// sounds so FBI
	this.tactics = undefined;
};

CityAttack.prototype.getName = function(){
	return this.name;
};
CityAttack.prototype.getType = function(){
	return this.type;
};

// This creates all the support plans needed
CityAttack.prototype.createSupportPlans = function(gameState, HeadQuarters, queue){
	//debug ("creating support plans for " +this.name);
	// TODO: finish the cases where no unit has been found.
	var numberOfPlans = 0;
	for (unitCat in this.categories)	// either infantry or cavalry or siege
	{
		if (unitCat !== "siege")	// there are immediate subcategories
		{
			for (attackType in this.categories[unitCat])
			{
				// okay here we're at the "this.unitCount.infantry.melee" step (for example)
				// check for further subdivision
				if (this.unitCount[unitCat][attackType].usesSubcategories === true)
				{
					// okay so we'll have archers, things like that
					for (var i = 0;i < this.unitCount[unitCat][attackType]["subCat"].length; i++)
					{
						var weaponType = this.unitCount[unitCat][attackType]["subCat"][i];
						if (this.unitCount[unitCat][attackType][weaponType]["preferedAmount"] === 0)
							continue;
						// Let's actually create the SupportPlan
						// priority. Since this is a subclass, we multiply with the "father" priority.
						var prio = this.unitCount[unitCat][attackType][weaponType]["priority"] * this.unitCount[unitCat][attackType]["priority"];
						var Plan = new UnitSupport(HeadQuarters, prio,this.unitCount[unitCat][attackType][weaponType]["preferedAmount"],this.queue);
						if (!Plan.setByCategory("Unit",unitCat,attackType,weaponType,gameState))// "Unit" since we don't particlarly want champions
						{
							// okay no fitting units have been found
							// TODO: depending on "fallback" method, do stuff here, such as going up one level.
							if (this.unitCount[unitCat][attackType][weaponType]["fallback"] === "abort")
							{
								return false;
							} else if (this.unitCount[unitCat][attackType][weaponType]["fallback"] == "upOneLevel")
							{	// in this case, we assume we're the only subcategory... And we put everything fom this specific category on the top level (eg swordsman to cav)
								if (this.unitCount[unitCat][attackType]["subCat"].length === 1)
								{
									this.unitCount[unitCat][attackType]["usesSubcategories"] = false;
									this.unitCount[unitCat][attackType]["minimalAmount"] = this.unitCount[unitCat][attackType][weaponType]["minimalAmount"];
									this.unitCount[unitCat][attackType]["preferedAmount"] = this.unitCount[unitCat][attackType][weaponType]["preferedAmount"];
									this.unitCount[unitCat][attackType]["priority"] = prio;
									this.unitCount[unitCat][attackType][weaponType] = undefined;
									var fallback = new UnitSupport(HeadQuarters, prio,this.unitCount[unitCat][attackType]["preferedAmount"],this.queue);
									if (!fallback.setByCategory("Unit",unitCat,attackType,"noSling_noPike",gameState))// "Unit" since we don't particlarly want champions
									{
										return false;	// can't go up forever
									} else {
										numberOfPlans++;
										this.unitCount[unitCat][attackType]["tempRapportA"] = 0;
										this.SupportPlan["" +attackType +"" +unitCat] = fallback;
										this.SupportPlan["" +attackType +"" +unitCat].setCountReferer(this.unitCount[unitCat][attackType]);
									}
								} else {
									return false;
								}
							} else {	// assume "ignore"
								this.unitCount[unitCat][attackType]["subCat"].splice(i,1);
								i--;
								this.unitCount[unitCat][attackType][weaponType] = undefined;
								if (this.unitCount[unitCat][attackType]["subCat"].length === 0)
									this.unitCount[unitCat][attackType]["usesSubcategories"] = false;
							}
						} else {
							numberOfPlans++;
							//assign the support plan
							this.unitCount[unitCat][attackType][weaponType]["tempRapportA"] = 0;
							this.SupportPlan["" +weaponType +"" +unitCat] = Plan;
							this.SupportPlan["" +weaponType +"" +unitCat].setCountReferer(this.unitCount[unitCat][attackType][weaponType]);
						}						
					}
				} else {
					if (this.unitCount[unitCat][attackType]["preferedAmount"] === 0)
						continue;
					// not using subcategories (such as "archer"... ) So we'll do it live
					var prio = this.unitCount[unitCat][attackType]["priority"];
					var Plan = new UnitSupport(HeadQuarters, prio,this.unitCount[unitCat][attackType]["preferedAmount"],this.queue);
					if (!Plan.setByCategory("Unit",unitCat,attackType,"noSling_noPike",gameState))// "Unit" since we don't particlarly want champions
					{
						// okay no fitting units have been found
						// TODO: depending on "fallback" method, do stuff here.
						if (this.unitCount[unitCat][attackType]["fallback"] == "abort")
						{
							return false;
						} else {
							this.unitCount[unitCat][attackType]["minimalAmount"] = 0;
							this.unitCount[unitCat][attackType]["currentAmount"] = 0;
							this.unitCount[unitCat][attackType]["preferedAmount"] = 0;
							this.unitCount[unitCat][attackType]["priority"] = 0;
						}
					} else {
						//assign the support plan
						numberOfPlans++;
						this.unitCount[unitCat][attackType]["tempRapportA"] = 0;
						this.SupportPlan["" +attackType +"" +unitCat] = Plan;
						this.SupportPlan["" +attackType +"" +unitCat].setCountReferer(this.unitCount[unitCat][attackType]);
					}
				}
			}// end for unitCat
		} else {
			if (this.unitCount[unitCat]["preferedAmount"] === 0)
				continue;
			// means Siege
			var prio = this.unitCount[unitCat]["priority"];
			var Plan = new UnitSupport(HeadQuarters, prio,this.unitCount[unitCat]["preferedAmount"],this.queue);
			if (!Plan.setByCategory("Unit",unitCat,"Unit","Unit",gameState))
			{
				// okay no fitting units have been found
				// TODO: depending on "fallback" method, do stuff here.
				if (this.unitCount[unitCat]["fallback"] === "abort")
				{
					return false;
				} else {
					this.unitCount[unitCat]["minimalAmount"] = 0;
					this.unitCount[unitCat]["currentAmount"] = 0;
					this.unitCount[unitCat]["preferedAmount"] = 0;
					this.unitCount[unitCat]["priority"] = 0;
				}
			} else {
				//assign the support plan
				numberOfPlans++;
				this.unitCount[unitCat]["tempRapportA"] = 0;
				this.SupportPlan["" +unitCat] = Plan;
				this.SupportPlan["" +unitCat].setCountReferer(this.unitCount[unitCat]);
			}
		}
	}
	if (numberOfPlans === 0)
		return false;
	// todo: run a cleanup function that removes any "This.unitCount" that are not used.
	
	this.SupportPlanArray = [];
	for (i in this.SupportPlan)
	{
		this.SupportPlanArray.push(this.SupportPlan[i]);
	}

	gameState.ai.queues[this.name] = this.queue;
	gameState.ai.queueManager.addQueue(this.name,this.queue);
	if (this.type === "harass_raid")
		gameState.ai.priorities[this.name] = 130;
	else
		gameState.ai.priorities[this.name] = 200;		
	return true;
};

CityAttack.prototype.getPlans = function(){
	var plans = [];
	for (i in this.SupportPlan)
	{
		plans.push(this.SupportPlan[i]);
	}
	return plans;
};
CityAttack.prototype.SortPlanArray = function(){
	this.SupportPlanArray.sort(function(a,b) { return (a.getPriority() - b.getPriority()); });
};

// Returns true if the attack can be executed at the current time
// Basically his checks we have enough units.
// We run a count of our units.

CityAttack.prototype.canStart = function(gameState, HeadQuarters){
	this.CountUnits(gameState,HeadQuarters);	
	var Enough = true;
	
	// loops through all catagories, checking for possible subcategories.
	for (unitCat in this.categories)
	{
		if (this.categories[unitCat] !== undefined)
		{
			for (attackType in this.categories[unitCat])
			{
				if (this.unitCount[unitCat][attackType].usesSubcategories == true)
				{
					for (var i = 0;i < this.unitCount[unitCat][attackType]["subCat"].length; i++)
					{
						//debug ("count for " +this.unitCount[unitCat][attackType]["subCat"][i] +" is " +this.unitCount[unitCat][attackType][this.unitCount[unitCat][attackType]["subCat"][i]]["currentAmount"]);
						if( this.unitCount[unitCat][attackType][this.unitCount[unitCat][attackType]["subCat"][i]]["currentAmount"] < 
						  this.unitCount[unitCat][attackType][this.unitCount[unitCat][attackType]["subCat"][i]]["minimalAmount"] )
							Enough = false;
					}
				} else
				{
					//debug ("count for " +unitCat +"-"+attackType +" is " +this.unitCount[unitCat][attackType]["currentAmount"]);
					if (this.unitCount[unitCat][attackType]["currentAmount"] < this.unitCount[unitCat][attackType]["minimalAmount"])
						Enough = false;
				}
			}
		} else
		{
			//debug ("count for " +unitCat +" is " +this.unitCount[unitCat][attackType]["currentAmount"]);
			if (this.unitCount[unitCat]["currentAmount"] < this.unitCount[unitCat]["minimalAmount"])
				Enough = false;
		}
	}
	// should also check wether we have a target or not, actually
	// returns true if Enough
	return Enough;
};

CityAttack.prototype.bringOutYourDead = function(HeadQuarters)
{
	var removeList = [];
	for (var idKey in this.idList){
		var id = this.idList[idKey];
		var ent = HeadQuarters.entity(id);
		if (ent === undefined){
			removeList.push(id);
		}
	}
	for (var i in removeList){
		this.idList.splice(this.idList.indexOf(removeList[i]),1);
	}
}
// Three returns possible: 1 is "keep going", 0 is "failed plan", 2 is "start"
CityAttack.prototype.updatePreparation = function(gameState, HeadQuarters,events) {
	Engine.ProfileStart("Update Preparation");
	// We'll first loop through our units to ensure they're all well and alive
	this.bringOutYourDead(HeadQuarters);
	
	if (gameState.ai.mainCounter % 3 == 0)
	{
		this.anyNotMinimal = false;
		// okay so we'll reassign indidivual priorities... We calculate two ratios: current / minimal and current / prefered.
		// we sort bu minimal priorities, UNLESS all of them have at least the minimal amount, in which case we sort by prefered.
		// the effect is that wd should strive for the balanced "minimal" composition, and then for the balanced "prefered" one.
		// this could handle a situation where the prefered amount is the minimal, things like that.
		// If the minimal amount is 0, then we'll set it to "1", and act like it had the minimal amount already.
		for (unitCat in this.categories)
		{
			if (this.categories[unitCat] !== undefined)
			{
				for (attackType in this.categories[unitCat])
				{
					if (this.unitCount[unitCat][attackType].usesSubcategories === true)
					{
						for (var i = 0;i < this.unitCount[unitCat][attackType]["subCat"].length; i++)
						{
							var weaponType = this.unitCount[unitCat][attackType]["subCat"][i];
							var Uniiiit = this.unitCount[unitCat][attackType][weaponType];
							var cur_min = 0;
							if (Uniiiit["minimalAmount"] !== 0)
								cur_min = Uniiiit["currentAmount"]/ Uniiiit["minimalAmount"];
							Uniiiit["tempRapportA"] = cur_min;
							Uniiiit["tempRapportB"] = 0;
							if (cur_min >= 1)
								Uniiiit["tempRapportB"] = Uniiiit["currentAmount"]/ Uniiiit["preferedAmount"];
							else
								this.anyNotMinimal = true;
							//debug ("value for " +unitCat +" " +weaponType +" is " +Uniiiit["tempRapportA"] + "--" +Uniiiit["tempRapportB"]);
						}
					} else
					{
						var cur_min = 0;
						var Uniiiit = this.unitCount[unitCat][attackType];
						if (Uniiiit["minimalAmount"] !== 0)
							cur_min = Uniiiit["currentAmount"]/ Uniiiit["minimalAmount"];
						Uniiiit["tempRapportA"] = cur_min;
						Uniiiit["tempRapportB"] = 0;
						if (cur_min >= 1)
							Uniiiit["tempRapportB"] = Uniiiit["currentAmount"]/ Uniiiit["preferedAmount"];
						else
							this.anyNotMinimal = true;
						//debug ("value for " +unitCat +" " +attackType +" is " +Uniiiit["tempRapportA"] + "--" +Uniiiit["tempRapportB"]);
					}
				}
			} else {
				var cur_min = 0;
				var Uniiiit = this.unitCount[unitCat];
				if (Uniiiit["minimalAmount"] !== 0)
					cur_min = Uniiiit["currentAmount"]/ Uniiiit["minimalAmount"];
				Uniiiit["tempRapportA"] = cur_min;
				Uniiiit["tempRapportB"] = 0;
				if (cur_min >= 1)
					Uniiiit["tempRapportB"] = Uniiiit["currentAmount"]/ Uniiiit["preferedAmount"];
				else
					this.anyNotMinimal = true;
				//debug ("value for " +unitCat +" is " +Uniiiit["tempRapportA"] + "--" +Uniiiit["tempRapportB"]);
			}
		}
	} else if ((gameState.ai.mainCounter + 1) % 3 === 0)
	{
		// okay let's actually assign the priorities
		for (unitCat in this.categories) {
			if (this.categories[unitCat] !== undefined) {
				for (attackType in this.categories[unitCat]) {
					if (this.unitCount[unitCat][attackType].usesSubcategories === true) {
						for (var i = 0;i < this.unitCount[unitCat][attackType]["subCat"].length; i++) {
							var weaponType = this.unitCount[unitCat][attackType]["subCat"][i];
							if (this.SupportPlan["" +weaponType +"" +unitCat] && this.anyNotMinimal)
								this.SupportPlan["" +weaponType +"" +unitCat].setPriority(this.unitCount[unitCat][attackType][weaponType]["tempRapportA"]);
							else if (this.SupportPlan["" +weaponType +"" +unitCat] && !this.anyNotMinimal)
								this.SupportPlan["" +weaponType +"" +unitCat].setPriority(this.unitCount[unitCat][attackType][weaponType]["tempRapportB"]);
						}
					} else {
						if (this.SupportPlan["" +attackType +"" +unitCat] && this.anyNotMinimal)
							this.SupportPlan["" +attackType +"" +unitCat].setPriority(this.unitCount[unitCat][attackType]["tempRapportA"]);
						else if (this.SupportPlan["" +attackType +"" +unitCat] && !this.anyNotMinimal)
							this.SupportPlan["" +attackType +"" +unitCat].setPriority(this.unitCount[unitCat][attackType]["tempRapportB"]);
					}
				}
			} else {
				if (this.SupportPlan["" +unitCat] && this.anyNotMinimal)
					this.SupportPlan["" +unitCat].setPriority(this.unitCount[unitCat]["tempRapportA"]);
				else if (this.SupportPlan["" +unitCat] && !this.anyNotMinimal)
					this.SupportPlan["" +unitCat].setPriority(this.unitCount[unitCat]["tempRapportB"]);
			}
		}
		this.SortPlanArray();
	} else if ((gameState.ai.mainCounter + 2 )% 3 === 0)
		this.AllToRallyPoint(gameState);

	// doing this already because it counts my unit, which is needed in the following loop.
	var canstart = this.canStart(gameState, HeadQuarters);

	Engine.ProfileStart("Creating units and looking through events");
	for (j in this.SupportPlanArray)
	{
		var plan = this.SupportPlanArray[j];
		// we don't have enough units
		// Through the magic of javascript references and using events, we use a very lightweight counting system.
		if (plan.getTrainingQueueAmount() + plan.getCountReferer()["currentAmount"] < plan.getCountReferer()["preferedAmount"])
		{
			// I intend to create units from this plan.
			if (plan.getQueue().countTotalQueuedUnits() < 13 && plan.getTrainingQueueAmount() < 6) {	
				var newUnit = HeadQuarters.findBestNewUnit(gameState, plan.getQueue(), plan.getUnits());
				if (newUnit)
				{
					plan.getQueue().addItem(new UnitTrainingPlan(gameState, newUnit, { "role" : "PreparationPlan_" +this.name }, 3 , plan.getPriority()));
					plan.TrainingQueueIncrement(3);
				}
			}
		}
	}
	// todo: remove redundancy with "bringoutyourdead" (note: redundancy not yet coded).
	for (var key in events){
		var e = events[key];
		if (e.type === "Attacked" && e.msg){
			if (this.idList.indexOf(e.msg.target) !== -1){
				var attacker = HeadQuarters.entity(e.msg.attacker);
				if (attacker && attacker.position()){
					var totor = new Tactics(gameState,HeadQuarters, this.idList,e.msg.attacker);
					break;
				}
			}
		}
		// TODO: add "strip_grade" just in case, though in this case it should not be a problem
		if (e.type === "TrainingFinished" && e.msg && e.msg.metada && e.msg.metadata === "PreparationPlan_" +this.name)
		{
			for (j in this.SupportPlanArray)
			{
				if (this.SupportPlanArray[j].getUnits().indexOf(gameState.getEntityById(e.msg.entities[0]).templateName()) !== -1)
				{
					this.SupportPlanArray[j].TrainingQueueDecrement(e.msg.entities.length);
					break;
				}
			}
		}
	}
	Engine.ProfileStop();
	Engine.ProfileStop();
	// we count our units by triggering "canStart"
	// returns false if we can no longer have time and cannot start.
	if (!this.mustStart(gameState))
		return 1;
	else if (canstart)
		return 2;
	else
		return 0;
};
CityAttack.prototype.isStarted = function(){
	return !(this.state == "unexecuted");
};
CityAttack.prototype.mustStart = function(gameState){
	// loops through all catagories, checking for possible subcategories.
	var max = true;
	for (unitCat in this.categories)
	{
		if (this.categories[unitCat] !== undefined)
		{
			for (attackType in this.categories[unitCat])
			{
				if (this.unitCount[unitCat][attackType].usesSubcategories == true)
				{
					for (var i = 0;i < this.unitCount[unitCat][attackType]["subCat"].length; i++)
					{
						if( this.unitCount[unitCat][attackType][this.unitCount[unitCat][attackType]["subCat"][i]]["currentAmount"] < 
						   this.unitCount[unitCat][attackType][this.unitCount[unitCat][attackType]["subCat"][i]]["preferedAmount"] )
							max = false;
					}
				} else
				{
					if (this.unitCount[unitCat][attackType]["currentAmount"] < this.unitCount[unitCat][attackType]["preferedAmount"])
						max = false;
				}
			}
		} else
		{
			//debug ("count for " +unitCat +" is " +this.unitCount[unitCat][attackType]["currentAmount"]);
			if (this.unitCount[unitCat]["currentAmount"] < this.unitCount[unitCat]["preferedAmount"])
				max = false;
		}
	}
	if (max == true)
		return true;
	return (this.maxPreparationTime + this.timeOfPlanStart < gameState.getTimeElapsed());
};
CityAttack.prototype.assignUnits = function(gameState){
	var self = this;
	var lookingFor = [];
	for (i in this.SupportPlan)
	{
		lookingFor = lookingFor.concat(this.SupportPlan[i].getUnits());
	}
	var unites = gameState.getOwnEntities();
	unites.forEach(function(ent) {
		if (self.idList.indexOf(ent.id) === -1 && lookingFor.indexOf(ent.templateName()) !== -1)
		{
			var okay = false;
			var data = ent.getMetadata("role");
			if(gameState.ai.status["defcon"] > 5 && gameState.ai.status["underAttack"] == false)
				if (data === "idleDefender" || data === "defender" )
					okay = true;
			if(data === "unusedSoldier" || data === "former" +self.type +"Plan" || data === "PreparationPlan_" +self.name)
				okay = true;
			if (okay)
			{
				self.ToRallyPoint(gameState,ent.id());
				self.idList.push(ent.id());
				ent.setMetadata("role", "ReadyForPlan_" +self.name);				
			}
		}
	});
	return true;
};
// this sends a unit by ID back to the "rally point"
CityAttack.prototype.ToRallyPoint = function(gameState,id)
{	
	// Move back to nearest rallypoint
	gameState.getEntityById(id).move(this.rallyPoint[0],this.rallyPoint[1]);
}
// this sends all units back to the "rally point"
CityAttack.prototype.AllToRallyPoint = function(gameState)
{
	for (i in this.idList)
	{
		this.ToRallyPoint(gameState,this.idList[i]);
	}
}

// Default target finder aims for conquest critical targets
CityAttack.prototype.defaultTargetFinder = function(gameState, HeadQuarters){
	var targets = undefined;
	
	targets = HeadQuarters.getEnemyBuildings(gameState,"ConquestCritical", this.targetPlayer);
	// If there are no critical structures, attack anything else that's critical
	if (targets.length == 0) {
		targets = gameState.entities.filter(function(ent) {
											return (ent.hasClass("ConquestCritical") && ent.owner() === this.targetPlayer && ent.position());
											});
	}
	// If there's nothing, attack anything else that's less critical
	if (targets.length == 0) {
		targets = HeadQuarters.getEnemyBuildings(gameState,"Town", this.targetPlayer);
	}
	if (targets.length == 0) {
		targets = HeadQuarters.getEnemyBuildings(gameState,"Village", this.targetPlayer);
	}
	return targets;
};
CityAttack.prototype.raidingTargetFinder = function(gameState, HeadQuarters, Target){
	var targets = undefined;
	if (Target == "villager")
	{
		// let's aim for any resource dropsite. We assume villagers are in the neighborhood (note: the human player could certainly troll us... small (scouting) TODO here.)
		targets = gameState.entities.filter(function(ent) {
				return (ent.hasClass("Structure") && ent.resourceDropsiteTypes() !== undefined && !ent.hasClass("CivCentre") && ent.owner() === this.targetPlayer && ent.position());
		});
		if (targets.length == 0) {
			targets = gameState.entities.filter(function(ent) {
												return (ent.hasClass("CivCentre") && ent.resourceDropsiteTypes() !== undefined && ent.owner() === this.targetPlayer  && ent.position());
												});
		}
		if (targets.length == 0) {
			// if we're here, it means they also don't have no CC... So I'll just take any building at this point.
			targets = gameState.entities.filter(function(ent) {
												return (ent.hasClass("Structure") && ent.owner() === this.targetPlayer  && ent.position());
												});
		}
		return targets;
	} else {
		return this.defaultTargetFinder(gameState, HeadQuarters);
	}
};

// Executes the attack plan, after this is executed the update function will be run every turn
// If we're here, it's because we have in our IDlist enough units.
// now the IDlist units are treated turn by turn
CityAttack.prototype.StartAttack = function(gameState, HeadQuarters){
	if(this.idList.length == 0)	// can happen when called for counterattacks
		return false;
	var pending = EntityCollectionFromIds(gameState, this.idList);

	var targets = [];
	if (this.type === "harass_raid")
		targets = this.targetFinder(gameState, HeadQuarters, "villager");
	else
	{
		targets = this.targetFinder(gameState, HeadQuarters);
	
		if (targets.length === 0){
			targets = this.defaultTargetFinder(gameState, HeadQuarters);
		}
	}
	
	// If we have a target, move to it
	if (targets.length) {
		// Add an attack role so the economic manager doesn't try and use them
		pending.forEach(function(ent) {
						ent.setMetadata("subrole", "attack");
						});
		
		var curPos = pending.getCentrePosition();
		
		// pick a random target from the list 
		var rand = Math.floor((Math.random()*targets.length));
		this.targetPos = undefined;
		var count = 0;
		while (!this.targetPos){
			var target = targets.toEntityArray()[rand];
			this.targetPos = target.position();
			count++;
			if (count > 1000){
				warn("No target with a valid position found");
				return false;
			}
		}
		
		// Find possible distinct paths to the enemy 
		var pathFinder = new PathFinder(gameState);
		var pathsToEnemy = pathFinder.getPaths(curPos, this.targetPos);
		if (! pathsToEnemy){
			pathsToEnemy = [[this.targetPos]];
		}
		this.path = [];
		
		if (this.type !== "harass_raid")
		{
			var rand = Math.floor(Math.random() * pathsToEnemy.length);
			this.path = pathsToEnemy[rand];
		} else {
			this.path = pathsToEnemy[Math.min(2,pathsToEnemy.length-1)];		
		}
		
		pending.move(this.path[0][0], this.path[0][1]);
		debug ("attacckakouyou");
		this.state = "walking";
	} else if (targets.length == 0 ) {
		gameState.ai.gameFinished = true;
		debug ("I do not have any target. So I'll just assume I won the game.");
		return true;
	}
	return true;
};

// Runs every turn after the attack is executed
CityAttack.prototype.update = function(gameState, HeadQuarters, events){
	Engine.ProfileStart("Update Attack");
	this.releaseAnyUnit(gameState,true);	// releases eventual units that would have spawned after the start
	this.bringOutYourDead(HeadQuarters);	// this will remove dead units from the attack.
	
	 if (this.idList.length == 0)
		 return 0;	// abort
	var units = EntityCollectionFromIds(gameState, this.idList);

	// we're marching towards the target
	// Check for attacked units in our band.
	var bool_attacked = false;
	// raids don't care about attacks much
	if (this.state === "walking" && this.type !== "harass_raid"){	// walking toward the target
		var sumAttackerPos = [0,0];
		var numAttackers = 0;
		// let's check if one of our unit is not under attack, by any chance.
		for (var key in events){
			var e = events[key];
			if (e.type === "Attacked" && e.msg){
				if (this.idList.indexOf(e.msg.target) !== -1){
					var attacker = HeadQuarters.entity(e.msg.attacker);
					if (attacker && attacker.position()){
						sumAttackerPos[0] += attacker.position()[0];
						sumAttackerPos[1] += attacker.position()[1];
						numAttackers += 1;
						bool_attacked = true;
						// todo: differentiate depending on attacker type... If it's a ship, let's not do anythin, a building, depends on the attack type/
						if (this.threatList.indexOf(e.msg.attacker) === -1)
						{
							var enemySoldiers = HeadQuarters.getEnemySoldiers().toEntityArray();
							for (j in enemySoldiers)
							{
								var enemy = enemySoldiers[j];
								if (enemy.position() === undefined)	// likely garrisoned
									continue;
								if (inRange(enemy.position(), attacker.position(), 1000) && this.threatList.indexOf(enemy.id()) === -1)
									this.threatList.push(enemy.id());
							}
							this.threatList.push(e.msg.attacker);
						}
					}
				}
			}
		}
		if (bool_attacked > 0){
			var avgAttackerPos = [sumAttackerPos[0]/numAttackers, sumAttackerPos[1]/numAttackers];
			units.move(avgAttackerPos[0], avgAttackerPos[1]);	// let's run towards it.
			this.tactics = new Tactics(gameState,HeadQuarters, this.idList,this.threatList,true);
			this.state = "attacking_threat";
		}
	}else if (this.state === "attacking_threat"){
		this.tactics.eventMetadataCleanup(events,HeadQuarters);
		var removeList = this.tactics.removeTheirDeads(HeadQuarters);
		this.tactics.removeMyDeads(HeadQuarters);
		for (var i in removeList){
			this.threatList.splice(this.threatList.indexOf(removeList[i]),1);
		}
		if (this.threatList.length <= 0)
		{
			this.tactics.disband(HeadQuarters,events);
			this.tactics = undefined;
			this.state = "walking";
			units.move(this.path[0][0], this.path[0][1]);
		}else
		{
			this.tactics.reassignAttacks(HeadQuarters);
		}
	}
	if (this.state === "walking"){
		if (SquareVectorDistance(units.getCentrePosition(), this.path[0]) < 400){
			this.path.shift();
			if (this.path.length > 0){
				units.move(this.path[0][0], this.path[0][1]);
			} else {
				debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination.");
				// we must assume we've arrived at the end of the trail.
				this.state = "arrived";
			}
		}
	}
	if (this.state === "arrived"){
		// let's proceed on with whatever happens now.
		// There's a ton of TODOs on this part.
		if (this.onArrivalReaction == "huntVillagers")
		{
			// let's get any villager and target them with a tactics manager
			var enemyCitizens = gameState.entities.filter(function(ent) {
					return (gameState.isEntityEnemy(ent) && ent.hasClass("Support") && ent.owner() !== 0  && ent.position());
				});
			var targetList = [];
			enemyCitizens.forEach( function (enemy) {
				if (inRange(enemy.position(), units.getCentrePosition(), 2500) && targetList.indexOf(enemy.id()) === -1)
					targetList.push(enemy.id());
			});
			if (targetList.length > 0)
			{
				this.tactics = new Tactics(gameState,HeadQuarters, this.idList,targetList);
				this.state = "huntVillagers";
			} else {
				this.state = "";
			}
		}
	}
	if (this.state === "huntVillagers")
	{
		this.tactics.eventMetadataCleanup(events,HeadQuarters);
		this.tactics.removeTheirDeads(HeadQuarters);
		this.tactics.removeMyDeads(HeadQuarters);
		if (this.tactics.isBattleOver())
		{
			this.tactics.disband(HeadQuarters,events);
			this.tactics = undefined;
			this.state = "";
			return 0;	// assume over
		} else
			this.tactics.reassignAttacks(HeadQuarters);
	}
	
	Engine.ProfileStop();
	return this.totalCountUnits(gameState,HeadQuarters);
};
CityAttack.prototype.CountUnits = function(gameState, HeadQuarters){
	//var totalcount = 0;
	Engine.ProfileStart("Counting units for plans");
	var unitsEnt = [];
	for (i in this.idList)
	{
		unitsEnt.push(gameState.getEntityById(this.idList[i]));
	}

	for (unitCat in this.categories)
	{
		if (this.categories[unitCat] !== undefined)
		{
			for (attackType in this.categories[unitCat])
			{
				if (this.unitCount[unitCat][attackType].usesSubcategories == true)
				{
					for (var i = 0;i < this.unitCount[unitCat][attackType]["subCat"].length; i++)
					{
						this.unitCount[unitCat][attackType][this.unitCount[unitCat][attackType]["subCat"][i]]["currentAmount"] = 0;
					}
				} else
					this.unitCount[unitCat][attackType]["currentAmount"] = 0;
			}
		} else
			this.unitCount[unitCat]["currentAmount"] = 0;
	}
	var self = this;
	// TODO: change this when Alpha 9 comes out
	unitsEnt.forEach(function(ent)
	{
		if (ent.hasClass("Cavalry"))
		{
			if (ent.hasClass("Melee") && self.unitCount.cavalry.melee["usesSubcategories"] === false)
				self.unitCount.cavalry.melee["currentAmount"]++;
			else if (ent.hasClass("Melee") && self.unitCount.cavalry.melee["usesSubcategories"] === true)
			{
				for (i in self.unitCount.cavalry.melee["subCat"])
				{
					if (isWeaponType(ent.templateName(), self.unitCount.cavalry.melee["subCat"][i]))
						self.unitCount.cavalry.melee[self.unitCount.cavalry.melee["subCat"][i]]["currentAmount"]++;
				}
			}
			else if (ent.hasClass("Ranged") && self.unitCount.cavalry.melee["usesSubcategories"] === false)
				self.unitCount.cavalry.ranged["currentAmount"]++;
			else if (ent.hasClass("Ranged") && self.unitCount.cavalry.melee["usesSubcategories"] === true)
			{
				for (i in self.unitCount.cavalry.ranged["subCat"])
					if (isWeaponType(ent.templateName(), self.unitCount.cavalry.ranged["subCat"][i]))
						self.unitCount.cavalry.ranged[self.unitCount.cavalry.ranged["subCat"][i]]["currentAmount"]++;
			}
		} else if (ent.hasClass("Infantry"))
		{
			if (ent.hasClass("Melee") && self.unitCount.infantry.melee["usesSubcategories"] === false)
				self.unitCount.infantry.melee["currentAmount"]++;
			else if (ent.hasClass("Melee") && self.unitCount.infantry.melee["usesSubcategories"] === true)
			{
				for (i in self.unitCount.infantry.melee["subCat"])
					if (isWeaponType(ent.templateName(), self.unitCount.infantry.melee["subCat"][i]))
						self.unitCount.infantry.melee[self.unitCount.infantry.melee["subCat"][i]]["currentAmount"]++;
			}
			else if (ent.hasClass("Ranged") && self.unitCount.infantry.melee["usesSubcategories"] === false)
				self.unitCount.infantry.ranged["currentAmount"]++;
			else if (ent.hasClass("Ranged") && self.unitCount.infantry.melee["usesSubcategories"] === true)
			{
				for (i in self.unitCount.infantry.ranged["subCat"])
					if (isWeaponType(ent.templateName(), self.unitCount.infantry.ranged["subCat"][i]))
						self.unitCount.infantry.ranged[self.unitCount.infantry.ranged["subCat"][i]]["currentAmount"]++;
			}
		} else if (ent.hasClass("Siege"))
		{
			self.unitCount.siege["currentAmount"]++;
		}
	});	
	Engine.ProfileStop();
};
CityAttack.prototype.totalCountUnits = function(gameState, HeadQuarters){
	var totalcount = 0;
	for (i in this.idList)
	{
		totalcount++;
	}
	return totalcount;
};
// reset any units
CityAttack.prototype.Abort = function(gameState){
	for (i in this.idList)
	{
		gameState.getEntityById(this.idList[i]).setMetadata("role","former" +this.type +"Plan");
	}
	this.idList = [];
	for (i in this.SupportPlan)
	{
		delete this.SupportPlan[i];
	}
	this.SupportPlanArray = [];
	delete gameState.ai.queues[this.name];
	gameState.ai.queueManager.removeQueue(this.name);
	delete this.queue;
};
CityAttack.prototype.releaseAnyUnit = function(gameState, onlyPreparation){
	var self = this;
	var soldats = gameState.getOwnEntitiesWithRole("PreparationPlan_" +this.name)
	soldats.forEach(function(ent) {
					ent.setMetadata("role","former" +self.type +"Plan");
					});
	soldats = gameState.getOwnEntitiesWithRole("ReadyForPlan_" +this.name)
	soldats.forEach(function(ent) {
					if (!onlyPreparation || ent.getMetadata("subrole") != "attack")
						ent.setMetadata("role","former" +self.type +"Plan");
					});
};
