/*
* 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.EnemyPosition;
import com.tyr.PositionUtil;
import com.tyr.Tyr;
import com.tyr.agents.Agent;
import com.tyr.buildingplacement.SpaceManager;
import com.tyr.unitgroups.MineralWorkers;

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

/**
 * This class implements a solution for constantly pushing the opponent.
 */
public class ClearExpandsSolution extends Solution
{
	List<Agent> units = new ArrayList<Agent>();
	
	/**
	 * List of all expands, in the order that they are encountered in a sweep around the map.
	 * Includes opponents main.
	 */
	private ArrayList<Position> orderedExpands;
	
	/**
	 * The position of the base we are currently moving to observe in the list of bases returned by EnemyManager.getOrderedexpands().
	 */
	private int currentPos;
	
	/**
	 * Are we going on a full assault for a scouted expand?
	 */
	private boolean fullAssault = false;
	
	/**
	 * Are we already attacking the cannon?
	 */
	private boolean attackingCannon;
	
	private int targetChangedFrame = -10000;
	
	/**
	 * This class implements a solution for constantly pushing the opponent.
	 * @param task The task that started this solution.
	 */
	public ClearExpandsSolution(Task task) 
	{
		super(task);
	}
	
	@Override
	public void onFrame(Game game, Player self, Tyr bot)
	{
		for (int i=units.size() - 1; i >= 0; i--)
			if (units.get(i) == null || units.get(i).isDead())
				units.remove(i);
		
		if (units.size() == 0)
			targetChangedFrame = game.getFrameCount();
		
		Position target = getTarget();
		
		int atCannons = 0;
		
		for (Agent agent: units)
		{
			for (EnemyPosition enemy : EnemyManager.getManager().enemyDefensiveStructures)
				if (agent.distanceSquared(enemy.pos) <= 450 * 450)
				{
					agent.drawCircle(Color.Orange, 16);
					atCannons++;
					fullAssault = true;
					targetChangedFrame = game.getFrameCount();
					break;
				}
		}
		
		if (atCannons > 0)
			DebugMessages.addMessage("At cannons: " + atCannons);
		
		attackingCannon = atCannons >= (attackingCannon ? 2 : 4);

		if (target != null)
		{
			for (Agent agent : units)
			{
				if (!attackingCannon)
				{
					boolean retreat = false;
					for (EnemyPosition enemy : EnemyManager.getManager().enemyDefensiveStructures)
						if (agent.distanceSquared(enemy.pos) <= 400 * 400)
							retreat = true;
					if (retreat)
					{
						agent.unit.move(Tyr.tileToPosition(self.getStartLocation()));
						agent.drawCircle(Color.Green, 6);
						continue;
					}
				}
				
				if (agent.unit.getType() == UnitType.Protoss_Dark_Templar)
				{
					boolean retreat = false;
					for (Unit enemy : EnemyManager.getEnemyUnits())
					{
						if (!enemy.getType().isDetector())
							continue;
						if (agent.distanceSquared(enemy) <= 400 * 400)
							retreat = true;
					}
					if (retreat)
					{
						agent.move(Tyr.getStartLocation());
						continue;
					}
				}
				
				agent.attack(target);
				game.drawLineMap(agent.unit.getX(), agent.unit.getY(), target.getX(), target.getY(), Color.Red);
			}
		}
	}
	
	/**
	 * Add an agent to the attack group with the attacking agents.
	 * @param unit The agent to be added.
	 */
	public void add(Agent agent)
	{
		units.add(agent);
	}

	/**
	 * Returns the number if agents sent out by this solution.
	 * @return Returns the number if agents sent out by this solution.
	 */
	public int size() 
	{
		return units.size();
	}
	
	public boolean done() 
	{
		return size() == 0;
	}
	
	/**
	 * Gets us the next target to harass.
	 */
	private Position getTarget()
	{
		if (orderedExpands == null && Tyr.bot.suspectedEnemy.size() == 1)
		{
			orderedExpands = (ArrayList<Position>)EnemyManager.getManager().getOrderedExpands();
			if (orderedExpands == null)
				return null;
			
			Position enemyNatural = SpaceManager.getEnemyNatural().getPosition();
			int enemyNat = 0;
			for (; enemyNat < orderedExpands.size() -1; enemyNat++)
				if (PositionUtil.distanceSq(enemyNatural, orderedExpands.get(enemyNat)) <= 100 * 100)
					break;
			orderedExpands.remove(enemyNat);
			
			currentPos = EnemyManager.getManager().getSelfPos();
			targetChangedFrame = Tyr.game.getFrameCount();
		}
		
		if (orderedExpands == null || orderedExpands.size() == 0)
			return null;
		
		
		Position curTarget = orderedExpands.get(currentPos);
		boolean isIsland = true;
		
		for (int i=0; i<= 30 && isIsland; i++)
		{
			for (BaseLocation loc : Tyr.bot.suspectedEnemy)
				if (loc.isIsland() && PositionUtil.distanceSq(curTarget, loc.getPosition()) <= 100 * 100)
				{
					currentPos = (currentPos + 1 + orderedExpands.size()) % orderedExpands.size();
					targetChangedFrame = Tyr.game.getFrameCount();
					curTarget = orderedExpands.get(currentPos);
					break;
				}
		}

		for (MineralWorkers base : Tyr.bot.workForce.mineralWorkers)
		{
			if (base.resourceDepot != null && PositionUtil.distanceSq(base.resourceDepot.getPosition(), curTarget) <= 100 * 100)
			{
				currentPos = (currentPos + 1 + orderedExpands.size()) % orderedExpands.size();
				targetChangedFrame = Tyr.game.getFrameCount();
				curTarget = orderedExpands.get(currentPos);
				break;
			}
		}
		if (Tyr.game.getFrameCount() - targetChangedFrame >= 750)
		{
			currentPos = (currentPos + 1 + orderedExpands.size()) % orderedExpands.size();
			targetChangedFrame = Tyr.game.getFrameCount();
			curTarget = orderedExpands.get(currentPos);
		}
		
		
		fullAssault = false;
		for (EnemyPosition pos : EnemyManager.getManager().enemyBuildingMemory)
			if (PositionUtil.distanceSq(pos.pos, curTarget) <= 500 * 500)
			{
				fullAssault = true;
				targetChangedFrame = Tyr.game.getFrameCount();
				return pos.pos;
			}

		for (Unit enemy : EnemyManager.getEnemyUnits())
			if (enemy.getType() != UnitType.Protoss_Observer && enemy.getType() != UnitType.Protoss_Dark_Templar && PositionUtil.distanceSq(enemy, curTarget) <= 400 * 400)
			{
				fullAssault = true;
				targetChangedFrame = Tyr.game.getFrameCount();
				return enemy.getPosition();
			}
		
		if (Tyr.game.isVisible(Tyr.positionToTile(curTarget)))
		{
			currentPos = (currentPos + 1 + orderedExpands.size()) % orderedExpands.size();
			targetChangedFrame = Tyr.game.getFrameCount();
		}
		
		return orderedExpands.get(currentPos);
	}
	
	/**
	 * Are we going on a full assault on the base?
	 * @return
	 */
	public boolean fullAssault()
	{
		return fullAssault;
	}

	public void clear(ArrayList<Agent> newGroup)
	{
		boolean gotZealot = false;
		for (int i = units.size() - 1; i >= 0; i--)
		{
			if (!gotZealot && units.get(i).unit.getType() == UnitType.Protoss_Zealot)
			{
				gotZealot = true;
				continue;
			}
			
			newGroup.add(units.get(i));
			units.remove(i);
		}
	}
}
