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

import java.util.ArrayList;
import java.util.List;

import com.tyr.DebugMessages;
import com.tyr.EnemyManager;
import com.tyr.Tyr;
import com.tyr.agents.Agent;
import com.tyr.buildingplacement.SpaceManager;
import com.tyr.unitgroups.HarassBaseGroup;
import com.tyr.unitgroups.MineralWorkers;

import bwapi.Color;
import bwapi.Game;
import bwapi.Player;
import bwapi.Position;
import bwapi.Unit;
import bwta.BaseLocation;

/**
 * This class implements a solution for harassing the opponents expands with vultures.
 */
public class VultureHarassSolution extends Solution 
{
	/**
	 * The unit group of units that perform the harass.
	 */
	private HarassBaseGroup harassGroup;
	
	/**
	 * The position of the base we are currently attacking in the list of bases returned by EnemyManager.getOrderedexpands().
	 */
	private int currentPos;
	
	/**
	 * The direction in which we are currently going through the list of bases returned by EnemyManager.getOrderedexpands().
	 */
	private int currentDir = 1;
	
	/**
	 * The position of the enemy's natural expand in the list of bases returned by EnemyManager.getOrderedexpands().
	 */
	private int enemyExpandPos = -1;
	
	/**
	 * The position of the base we currently intend to harass.
	 */
	private Position target;
	
	/**
	 * This class implements a solution for harassing the opponents expands with vultures.
	 * @param task The task that started this solution.
	 */
	public VultureHarassSolution(Task task) 
	{
		super(task);
		determineTarget();
		harassGroup = new HarassBaseGroup(target);
	}
	
	/**
	 * This class implements a solution for harassing the opponents expands with vultures.
	 * @param task The task that started this solution.
	 * @param startDir The direction in which we perform our first attack, either 1 or -1.
	 */
	public VultureHarassSolution(Task task, int startDir) 
	{
		super(task);
		this.currentDir = startDir;
		determineTarget();
		harassGroup = new HarassBaseGroup(target);
	}
	
	@Override
	public void onFrame(Game game, Player self, Tyr bot)
	{
		// See if we need to change the target, and if so give the new target to the harass group.
		if (determineTarget())
			harassGroup.setTarget(target);
		
		// Manage the harass group.
		harassGroup.cleanup();
		harassGroup.onFrame(game, self, bot);
	}

	/**
	 * Adds a vulture to the group of harassing vultures.
	 * @param agent The vulture to be added.
	 */
	public void add(Agent agent)
	{
		harassGroup.add(agent);
	}

	/**
	 * The number of vultures currently harassing.
	 * @return The number of vultures currently harassing.
	 */
	public int vultureCount() 
	{
		return harassGroup.units.size();
	}
	
	/**
	 * Determines the base we want to attack.
	 * @return Whether the method call actually changes the target.
	 */
	private boolean determineTarget()
	{
		boolean result = false;
		
		ArrayList<Position> orderedExpands = EnemyManager.getManager().getOrderedExpands();
		
		if (orderedExpands != null)
		{
			for(int i=0; i<orderedExpands.size(); i++)
			{
				Position pos = orderedExpands.get(i);
				Tyr.drawCircle(pos, Color.Red, 32);
				Tyr.game.drawTextMap(pos.getX(), pos.getY(), i + "");
			}
			
			if (target != null)
				Tyr.drawCircle(target, Color.Red);
		}
		// If the target has not previously been initialized, initalize it.
		if (target == null)
		{
			if (orderedExpands == null)
			{
				DebugMessages.addMessage("orderedExpands is null.");
				return false;
			}
			
			currentPos = EnemyManager.getManager().getSelfPos();
			if(currentPos != -1)
			{
				target = orderedExpands.get(currentPos);
				result = true;
			}
		}
		
		// Determine whether we are done harassing the current target.
		for(int i=0; i < 2*orderedExpands.size(); i++)
		{
			boolean isIsland = false;
			for (BaseLocation loc : Tyr.bot.expands)
			{
				if (!loc.isIsland())
					continue;
				if (loc.getDistance(target) <= 128)
					isIsland = true;
			}
			
			// If the target is invisible, we are not done.
			if (!isIsland && target != null && !Tyr.game.isVisible(Tyr.positionToTile(target)))
				break;
			
			// If the vultures are too far away from that base, we do not yet look for a new target.
			boolean closeEnough = false;
			
			// If we have a base there, we do not need to harass it.
			for (MineralWorkers base : Tyr.bot.workForce.mineralWorkers)
				if (base.resourceDepot != null && base.resourceDepot.getDistance(target) < 100)
					closeEnough = true;
			
			if (!isIsland && harassGroup != null && !closeEnough)
			{
				for(Agent agent:this.harassGroup.units)
				{
					if (agent == null)
						continue;
					
					if (agent.distanceSquared(target) < 128 * 128)
						closeEnough = true;
				}
			}
			if (!isIsland && !closeEnough)
				break;
			
			List<Unit> unitsInRange = Tyr.game.getUnitsInRadius(target, 320);
			
			// If there are workers left at the enemy base, we are not done.
			for(Unit unit : unitsInRange)
			{
				if (!unit.getPlayer().isEnemy(Tyr.self))
					continue;
				if(unit.getType().isWorker())
				{
					DebugMessages.addMessage("Worker target found.");
					return result;
				}
			}
			
			// We are done with the current target, get the next one.
			nextTarget();
			
			// We skip the enemy's natural expand.
			determineEnemyExpand();
			while (currentPos == enemyExpandPos)
				nextTarget();
			result = true;
		}
		
		return result;
	}

	private void determineEnemyExpand()
	{
		if (enemyExpandPos >= 0 || Tyr.bot.suspectedEnemy.size() > 1)
			return;

		ArrayList<Position> orderedExpands = EnemyManager.getManager().getOrderedExpands();
		
		if (orderedExpands == null)
			return;
		
		Position exit = SpaceManager.getExit(Tyr.bot.suspectedEnemy.get(0).getPosition());
		int closest = -1;
		int bestDist = Integer.MAX_VALUE;
		for (int i=0; i<orderedExpands.size(); i++)
		{
			int dist = (int) orderedExpands.get(i).getDistance(exit);
			if (dist < bestDist)
			{
				closest = i;
				bestDist = dist;
			}
		}
		enemyExpandPos = closest;
	}

	/**
	 * Gets us the next target to harass.
	 */
	private void nextTarget()
	{
		ArrayList<Position> orderedExpands = EnemyManager.getManager().getOrderedExpands();
		if (orderedExpands == null)
			return;
		
		currentPos += currentDir;
		
		// If we are at the end of the list of expands, we go back.
		if (currentPos >= orderedExpands.size() || currentPos < 0)
		{
			currentDir *= -1;
			currentPos += 2*currentDir;
		}
		
		target = orderedExpands.get(currentPos);
	}
	
}
