/*
* Tyr is an AI for StarCraft: Broodwar, 
* 
* Please visit https://github.com/SimonPrins/Tyr for further information.
* 
* Copyright 2015 Simon Prins
*
* This file is part of Tyr.
* Tyr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* Tyr 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with Tyr.  If not, see http://www.gnu.org/licenses/.
*/


package com.tyr.unitgroups;

import com.tyr.BWTAProxy;
import com.tyr.EnemyManager;
import com.tyr.EnemyPosition;
import com.tyr.Settings;
import com.tyr.Tyr;
import com.tyr.agents.Agent;
import com.tyr.agents.BCAttack;
import com.tyr.agents.BCDefend;
import com.tyr.agents.None;

import bwapi.Color;
import bwapi.Game;
import bwapi.Player;
import bwapi.Position;

/**
 * This unit group manages a squad of Battlecruisers which has been sent out to attack.
 * @author Simon
 *
 */
public class BCSquad extends UnitGroup
{
	/**
	 * This unit group manages a squad of Battlecruisers which has been sent out to attack.
	 * @param rejects The OutOfJob object to which units are sent that are no longer needed.
	 */
	public BCSquad(OutOfJob rejects) 
	{
		super(rejects);
	}
	
	boolean attackMode = false;
	
	/**
	 * Is this squad currently retreating?
	 */
	boolean retreatMode = false;
	
	/**
	 * The position where we will attack.
	 */
	public Position target = null;
	
	/**
	 * The minimum number of battlecruisers we want alive before we decide that all should flee.
	 */
	public static int fleeMinimum = 3;
	
	/**
	 * The minimum number of battlecruiers with which we are willing to move out.
	 */
	public static int moveOutMinimum = 6;
	
	/**
	 * The maximum number of battlecruisers with which we are willing to move out.
	 */
	public static int maximum = 8;
	
	@Override
	public void onFrame(Game game, Player self, Tyr bot)
	{
		if(BWTAProxy.initialized && units.size() > 0)
		{
			if(target == null)
				getDefenseTarget();
		}
		
		// Determine if we need to defend.
		boolean defend = EnemyManager.getManager().getInvader() != null;
		
		// Determine if we will switch to attack mode.
		boolean attack = EnemyManager.getManager().getInvader() == null && units.size() >= moveOutMinimum;

		
		if(attack)
		{
			for(Agent agent : units)
			{
				if(agent.unit.getHitPoints() < agent.unit.getType().maxHitPoints())
				{
					attack = false;
					break;
				}
			}
		}
		
		// Draw debug information.
		if (defend)
		{
			for(Agent agent : units)
				Tyr.drawCircle(agent.unit.getPosition(), Color.Green, 32);
		}
		else if (!attackMode && !retreatMode)
		{
			for(Agent agent : units)
				Tyr.drawCircle(agent.unit.getPosition(), Color.Yellow, 32);
		}
		else if (retreatMode)
		{
			for(Agent agent : units)
				Tyr.drawCircle(agent.unit.getPosition(), Color.White, 32);
		}
		else if (attackMode)
		{
			for(Agent agent : units)
				Tyr.drawCircle(agent.unit.getPosition(), Color.Red, 32);
		}
		
		if (attackMode)
		{
			if (units.size() < fleeMinimum)
			{
				// If we have not enough battlecruisers left, we flee.
				attackMode = false;
				retreatMode = true;
				getDefenseTarget();
			}
			else
			{
				for(Agent unit : units)
					if (unit.unit.getHitPoints() <= 125)
					{
						attackMode = false;
						retreatMode = true;
						getDefenseTarget();
					}
			}
		}
		
		if (retreatMode)
		{
			if(target != null)
				Tyr.drawCircle(target, Color.Blue, 160);
			boolean returned = true;
			for(Agent agent : units)
			{
				// See if all battlecruisers have arrived at the base.
				agent.order(new None(agent));
				if(target != null && agent.distanceSquared(target) >= 200*200)
				{
					returned = false;
					Tyr.drawCircle(agent.unit.getPosition(), Color.Red, 6);
					game.drawLineMap(target.getX(), target.getY(), agent.unit.getX(), agent.unit.getY(), Color.Red);
				}
				else
				{
					Tyr.drawCircle(agent.unit.getPosition(), Color.Green, 6);
				}
			}
			
			if(returned)
				retreatMode = false;
			else
			{
				// Send the battlecruisers home.
				for(Agent agent : units)
				{
					agent.drawCircle(Color.White);
					agent.unit.move(target);
				}
				return;
			}
		}
		
		// Send idle battlecruisers to the same spot as our idle defending units.
		if(!defend && !attackMode && !attack)
		{
			if(Settings.getRallyPoint() != null && target != Settings.getRallyPoint())
				target = Settings.getRallyPoint();
		}
		
		boolean targetChanged = false;
		
		if(BWTAProxy.initialized && !attackMode && attack)
			attackMode = true;
		
		// See if we need to find a new attack target.
		if (!defend && BWTAProxy.initialized && attackMode && EnemyManager.getManager().getInvader() == null)
			targetChanged = acquireTarget();
		
				
		if(!defend && BWTAProxy.initialized && attackMode)
		{
			if (target != null)
				Tyr.drawCircle(target, Color.Orange, 128);
			
			// Order the battlecruisers to attack.
			if (targetChanged && target != null)
				for(Agent agent : units)
					agent.order(new BCAttack(agent, target));
		}
		else
		{
			// Order the battlecruisers to defend.
			if (target != null)
			{
				Tyr.drawCircle(target, Color.Blue, 160);
				for(Agent agent : units)
					agent.order(new BCDefend(agent, target));
			}
		}
	}
	
	/**
	 * See where we gather to defend.
	 * @return True iff the target has changed since last frame.
	 */
	public boolean getDefenseTarget()
	{
		Position newTarget = null;
		if(Settings.getRallyPoint() != null)
			newTarget = Settings.getRallyPoint();
		else if (Tyr.bot.defensiveStructures.get(0).defences.size() > 0)
			newTarget = Tyr.bot.defensiveStructures.get(0).defences.get(0).getPosition();
		else
			newTarget = Tyr.bot.defensiveStructures.get(0).getDefensePos();
		
		if (newTarget == null)
			return false;
		
		if(target != null && newTarget.getX() == target.getX() && newTarget.getY() == target.getY())
			return false;
		
		target = newTarget;
		return true;
	}
	
	/**
	 * Update the target for the attack.
	 * @return True iff the target has changed.
	 */
	public boolean acquireTarget()
	{
		if (target != null)
	    	for(EnemyPosition p : EnemyManager.getManager().enemyBuildingMemory)
	    		if (p.pos.getX() == target.getX() && p.pos.getY() == target.getY())
	    			return false;
		
		Position newTarget = null;
		Position mainPos = Tyr.bot.suspectedEnemy.get(0).getPosition();
		if (Tyr.bot.suspectedEnemy.size() == 0)
			return false;
		int dist = 0;

		for (EnemyPosition p : EnemyManager.getManager().enemyBuildingMemory)
		{
			int newDist = (int)p.pos.getDistance(mainPos);
			if (newDist > dist)
			{
				newTarget = p.pos;
				dist = newDist;
			}
		}
		
		if(newTarget == null)
			return false;
		
    	if (target != null && target.getX() == newTarget.getX() && target.getY() == newTarget.getY())
    		return false;
    	
    	target = newTarget;
    	return true;
	}
}
