/*
* 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 java.util.List;

import com.tyr.DebugMessages;
import com.tyr.EnemyManager;
import com.tyr.Tyr;
import com.tyr.agents.Agent;
import com.tyr.agents.None;

import bwapi.Color;
import bwapi.Game;
import bwapi.Player;
import bwapi.Position;
import bwapi.Unit;
import bwapi.UnitType;
import bwapi.WeaponType;


/**
 * A unit group for harassing a certain base.
 * @author Simon
 *
 */
public class HarassBaseGroup extends UnitGroup
{
	/**
	 * The position of the base we want to harass.
	 */
	private Position target;
	
	/**
	 * Do we retreat after each attack?
	 */
	private boolean retreat = true;
	
	/**
	 * The next enemy we intend to kill.
	 */
	private Unit killEnemy;

	/**
	 * A unit group for harassing a certain base.
	 * @param target The position of the base we want to harass.
	 */
	public HarassBaseGroup(Position target) 
	{
		super(Tyr.bot.hobos);
		
		this.target = target;
	}
	
	/**
	 * A unit group for harassing a certain base.
	 * @param target The position of the base we want to harass.
	 * @param retreat Do we retreat after each attack?
	 */
	public HarassBaseGroup(Position target, boolean retreat) 
	{
		super(Tyr.bot.hobos);
		
		this.target = target;
		this.retreat = false;
	}
	
	/**
	 * Setter for the position of the base we want to harass.
	 */
	public void setTarget(Position target)
	{
		this.target = target;
	}

	@Override
	public void onFrame(Game game, Player self, Tyr bot)
	{
		// If the previously selected enemy is already dead we can select a new one.
		if (killEnemy != null)
			if (killEnemy.getHitPoints() <= 0 || killEnemy.getRemoveTimer() != 0 
					|| !killEnemy.exists() || killEnemy.getPlayer() == game.self())
				killEnemy = null;
		
		if (killEnemy != null)
		{
			boolean inRange = false;
			
			// If the unit is a worker and it is close enough to our target we will continue to attack it.
			if (target != null && killEnemy.getType().isWorker() && killEnemy.getDistance(target) <= 352)
				inRange = true;
			else
			{
				// See if the unit is in weapons range of one of our harassing units.
				for(Agent agent : units)
				{
					int range = 0;
					if (killEnemy.getType().isFlyer() && agent.unit.getType().airWeapon() != WeaponType.None)
						range = agent.unit.getType().airWeapon().maxRange();
					if (!killEnemy.getType().isFlyer() && agent.unit.getType().groundWeapon() != WeaponType.None)
						range = agent.unit.getType().groundWeapon().maxRange();
					if (agent.distanceSquared(killEnemy) <= range * range)
					{
						inRange = true;
						break;
					}
				}
			}
			
			if(!inRange)
				killEnemy = null;
		}
		
		double distanceSq = Integer.MAX_VALUE;
		if (killEnemy == null || killEnemy.getType().isWorker())
		{
			// See if an enemy is inside weapons range.
			for(Agent agent : units)
			{
				int rangeSq = agent.unit.getType().groundWeapon().maxRange() * agent.unit.getType().groundWeapon().maxRange();
				agent.drawCircle(Color.Purple, agent.unit.getType().groundWeapon().maxRange());
				
				for(Unit unit : EnemyManager.getEnemyUnits())
				{
					// We do not attack buildings.
					if (unit.getType().isBuilding() && unit.getType().groundWeapon().maxRange() == 0)
						continue;
					
					// If we cannot attack flyers, we ignore them.
					if (unit.getType().isFlyer() && agent.unit.getType().airWeapon() == WeaponType.None)
						continue;
					
					// We do not attack observers either.
					if (unit.getType() == UnitType.Protoss_Observer)
						continue;
					
					double dist = agent.distanceSquared(unit);
					// Enemy is too far away to attack.
					if (dist > rangeSq)
						continue;
					
					Tyr.drawCircle(unit.getPosition(), Color.Purple);
					
					// Attack the closest enemy.
					if (dist < distanceSq)
					{
						distanceSq = dist;
						killEnemy = unit;
					}
				}
			}
		}
		else
			DebugMessages.addMessage("Already have a target.");
		
		if (killEnemy == null && target != null)
		{
			// see if there is a worker we want to kill.
			List<Unit> workerTargets = game.getUnitsInRadius(target, 352);
			for(Unit unit : workerTargets)
			{
				if (!unit.getType().isWorker() || !unit.getPlayer().isEnemy(self))
					continue;
				
				for(Agent agent : units)
				{
					double dist = agent.distanceSquared(unit);
					if (unit.getPlayer().isEnemy(self) && dist < distanceSq)
					{
						distanceSq = dist;
						killEnemy = unit;	
					}
				}
			}
		}
		
		// If we have an enemy to kill, we kill the enemy.
		if (killEnemy != null)
		{	
			for (Agent agent : units)
			{
				game.drawLineMap(agent.unit.getX(), agent.unit.getY(), killEnemy.getX(), killEnemy.getY(), Color.Red);
				
				if (agent.unit.getGroundWeaponCooldown() > 1 && retreat)
					agent.unit.move(Tyr.tileToPosition(self.getStartLocation()));
				else if(!agent.unit.isStartingAttack() && !agent.unit.isAttackFrame() && ! agent.unit.isAttacking())
					agent.unit.attack(killEnemy);
			}
			return;
		}
		
		// If we do not have an enemy to kill, we move to the target.
		if (target != null)
		{
			for (Agent agent : units)
			{
				agent.unit.move(target);

				game.drawLineMap(agent.unit.getX(), agent.unit.getY(), target.getX(), target.getY(), Color.Blue);
			}
		}
	}
	
	/**
	 * Adds an agent to the list of agents.
	 */
	@Override public void add(Agent agent)
	{
		super.add(agent);
		agent.order(new None(agent));
		agent.unit.stop();
	}
	
	/**
	 * Clears the list of units, so that the group can be removed.
	 */
	public void clear() 
	{
		for(int i = units.size(); i >= 0; i--)
		{
			this.rejects.add(units.get(i));
			units.remove(i);
		}
	}

}
