/*
* 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.agents;

import java.util.List;

import com.tyr.Tyr;

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


/**
 * A command for a Battlecruiser to attack the enemy.
 * @author Simon
 *
 */
public class BCAttack extends Command
{
	/**
	 * The position where the Nattlecruiser will attack.
	 */
	private Position target;
	
	/**
	 * Do we stutterstep the Battlecruisers?
	 */
	private static final boolean stutterstep = false;
	
	/**
	 * A command for a Battlecruiser to attack the enemy.
	 * @param agent The Battlecruiser which will attack the enemy.
	 * @param target The position where the Nattlecruiser will attack.
	 */
	public BCAttack(Agent agent, Position target)
	{
		super(agent);
		this.target = target;
	}
	
	@Override
	public void execute(Game game, Player self, Tyr bot) 
	{
		agent.drawCircle(Color.Red);
		
		attack();
	}

	@SuppressWarnings("unused")
	/**
	 * Old method for managing the attack.
	 * No longer used.
	 */
	private void attack()
	{
		if (agent.distanceSquared(target) <= 128*128)
			return;
		
		Unit yamatoTarget = agent.getYamatoTarget();
		if(yamatoTarget != null)
		{
			agent.unit.useTech(TechType.Yamato_Gun, yamatoTarget);
			return;
		}
		
		Order order = agent.unit.getOrder(); 
		Position orderTarget = agent.unit.getOrderTargetPosition();
		
		Unit closest = getClosest();
		double distSq = UnitType.Terran_Battlecruiser.groundWeapon().maxRange() - 64;
		distSq *= distSq;
		
		if (stutterstep && closest != null && agent.unit.getGroundWeaponCooldown() > 1 && agent.unit.getAirWeaponCooldown() > 1 && agent.distanceSquared(closest) <= distSq)
		{
			agent.unit.move(Tyr.tileToPosition(Tyr.game.self().getStartLocation()));
		}
		else if (agent.unit.isHoldingPosition() || agent.unit.isIdle() || (stutterstep && (agent.unit.getGroundWeaponCooldown() == 1 || agent.unit.getAirWeaponCooldown() == 1)))
		{
			agent.unit.attack(target);
		}
		else 
		{
			double dist = target.getDistance(orderTarget);
			if (order == Order.AttackMove 
					&& orderTarget != null
					&& agent.unit.getTarget() == null
					&&
				(Math.abs(target.getX() - orderTarget.getX()) >= 10 
					|| Math.abs(target.getY() - orderTarget.getY()) >= 10
					|| dist >= 10))
			{
				agent.unit.attack(target);
			}
		}
	}
	
	
	/**
	 * Gets the closest enemy unit.
	 * @return
	 */
	private Unit getClosest()
	{
		Game game = Tyr.game;
		List<Unit> inRange = game.getUnitsInRadius(agent.unit.getPosition(), UnitType.Terran_Battlecruiser.groundWeapon().maxRange());
		Unit result = null;
		double distSq = game.mapHeight()*32 + game.mapWidth()*32;
		distSq = distSq * distSq;
		
		// Loop over the enemy units...
		for (Unit unit : inRange)
		{
			if (!unit.getPlayer().isEnemy(game.self()))
				continue;
			
			if (unit.getType().airWeapon() == WeaponType.None)
				continue;
			
			double newDist = agent.distanceSquared(unit);
			
			// ... To find the closest.
			if (newDist < distSq)
			{
				result = unit;
				distSq = newDist;
			}
		}
		if (result != null)
			return result;
		
		// Loop over the enemy units...
		for (Unit unit : inRange)
		{
			if (!unit.getPlayer().isEnemy(game.self()))
				continue;
			
			double newDist = agent.distanceSquared(unit);
			
			// ... To find the closest.
			if (newDist < distSq)
			{
				result = unit;
				distSq = newDist;
			}
		}
		
		return result;
	}

	@Override
	public boolean replace(Command command) 
	{
		if (!command.getClass().equals(BCAttack.class))
			return true;
		
		return ((BCAttack)command).target.getX() != target.getX() || ((BCAttack)command).target.getY() != target.getY();
	}
	
}
