/*
* 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.EnemyManager;
import com.tyr.OrderedExpands;
import com.tyr.PositionUtil;
import com.tyr.Tyr;
import com.tyr.agents.Agent;
import com.tyr.agents.None;
import com.tyr.unitgroups.UnitGroup;

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

/**
 * This class implements a solution for constantly pushing the opponent.
 */
public class ObserverSolution extends Solution
{
	private static ObserverSolution solution;
	
	List<Agent> observers = new ArrayList<Agent>();
	
	private Agent baseObserver;
	private Agent armyObserver;
	private Agent cwObserver;
	private Agent ccwObserver;
	
	/**
	 * The OrderedExpands object for our clockwise scouting observer.
	 */
	private OrderedExpands cwExpands = new OrderedExpands(true, false, true, true);
	
	/**
	 * This class implements a solution for constantly pushing the opponent.
	 * @param task The task that started this solution.
	 */
	public ObserverSolution(Task task) 
	{
		super(task);
		solution = this;
	}
	
	@Override
	public void onFrame(Game game, Player self, Tyr bot)
	{
		if (baseObserver != null && baseObserver.isDead())
			baseObserver = null;
		if (armyObserver != null && armyObserver.isDead())
			armyObserver = null;
		if (cwObserver != null && cwObserver.isDead())
			cwObserver = null;
		if (ccwObserver != null && ccwObserver.isDead())
			ccwObserver = null;
		
		if (armyObserver == null && observers.size() > 0)
		{
			armyObserver = observers.get(observers.size()-1);
			observers.remove(observers.size()-1);
		}
		
		if (cwObserver == null && observers.size() > 0)
		{
			cwObserver = observers.get(observers.size()-1);
			observers.remove(observers.size()-1);
		}
		
		
		for (int i=observers.size() - 1; i >= 0; i--)
			if (observers.get(i) == null || observers.get(i).isDead())
				observers.remove(i);
		
		if (armyObserver != null)
		{
			int distSq = 400*400;
			Position enemyPos = null;
			for (Unit enemy : EnemyManager.getEnemyUnits())
			{
				if (enemy.getType() == UnitType.Zerg_Lurker || enemy.getType() == UnitType.Protoss_Dark_Templar)
				{
					int newDistSq = armyObserver.distanceSquared(enemy);
					if (newDistSq < distSq)
					{
						boolean unitClose = false;
						for (Unit unit : Tyr.self.getUnits())
						{
							if (unit.getType().groundWeapon() == WeaponType.None)
								continue;
							if (unit.getType().isBuilding() || unit.getType().isWorker())
								continue;
							if (!unit.isCompleted())
								continue;
							if (PositionUtil.distanceSq(unit, enemy) <= 300 * 300)
							{
								unitClose = true;
								break;
							}
						}
						if (unitClose)
						{
							distSq = newDistSq;
							enemyPos = enemy.getPosition();
						}
					}
				}
			}
			if (enemyPos != null)
			{
				armyObserver.unit.move(enemyPos);
				game.drawLineMap(armyObserver.unit.getX(), armyObserver.unit.getY(), enemyPos.getX(), enemyPos.getY(), Color.Green);
			}
			else
			{
				Position armyPos = getArmyPos();
				if (armyPos != null)
				{
					armyObserver.unit.move(armyPos);
					game.drawLineMap(armyObserver.unit.getX(), armyObserver.unit.getY(), armyPos.getX(), armyPos.getY(), Color.Green);
				}
			}
		}
		
		Position cwPos = null;
		if (cwObserver != null)
			cwPos = getTarget();
		if (cwPos != null)
		{
			cwObserver.unit.move(cwPos);
			game.drawLineMap(cwObserver.unit.getX(), cwObserver.unit.getY(), cwPos.getX(), cwPos.getY(), Color.Green);
		}
	}
	
	/**
	 * Add an agent to the attack group with the attacking agents.
	 * @param unit The agent to be added.
	 */
	public void add(Agent agent)
	{
		observers.add(agent);
	}
	
	public Position getArmyPos()
	{
		Unit closest = null;
		int distance = Integer.MAX_VALUE;
		Position target = null;
		if (Tyr.bot.suspectedEnemy.size() > 0)
			target = Tyr.bot.suspectedEnemy.get(0).getPosition();
		else if (EnemyManager.getManager().enemyBuildingMemory.size() > 0)
			target = EnemyManager.getManager().enemyBuildingMemory.iterator().next().pos;
		
		if (target != null)
		{
			for (Unit unit : Tyr.self.getUnits())
			{
				if (unit.getType().groundWeapon() == WeaponType.None)
					continue;
				if (unit.getType().isBuilding() || unit.getType().isWorker())
					continue;
				if (unit.getType() == UnitType.Protoss_Zealot)
					continue;
				if (unit.getType() == UnitType.Protoss_Dark_Templar)
					continue;
				if (!unit.isCompleted())
					continue;
				if (unit.getDistance(target) < distance)
				{
					distance = unit.getDistance(target);
					closest = unit;
				}
			}
		}
		
		if (closest == null)
			return null;
		else
			return closest.getPosition();
	}
	
	/**
	 * This method is called when this solution is no longer needed.
	 */
	public void done(UnitGroup unitGroup) 
	{
		for(Agent unit : observers)
		{
			unitGroup.add(unit);
			unit.order(new None(unit));
		}
		observers = new ArrayList<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() 
	{
		int result = observers.size();
		if (armyObserver != null)
			result++;
		if (baseObserver != null)
			result++;
		if (cwObserver != null)
			result++;
		if (ccwObserver != null)
			result++;
		return result;
	}
	
	public boolean done() 
	{
		return size() == 0;
	}
	
	/**
	 * Gets us the next target to harass.
	 */
	private Position getTarget()
	{
		if (!cwExpands.tryInitialize())
			return null;
		
		if (Tyr.game.isVisible(Tyr.positionToTile(cwExpands.getPos())))
			cwExpands.next();
		
		return cwExpands.getPos();
	}

	public static Agent getArmyObserver()
	{
		if (solution == null)
			return null;
		return solution.armyObserver;
	}
}
