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

import com.tyr.DebugMessages;
import com.tyr.EnemyManager;
import com.tyr.EnemyPosition;
import com.tyr.Settings;
import com.tyr.Tyr;
import com.tyr.agents.Agent;

import bwapi.Game;
import bwapi.Player;
import bwapi.Race;
import bwapi.Unit;
import bwapi.UnitType;


/**
 * This unit group manages a group of workers that have been pulled to defend.
 * @author Simon
 *
 */
public class DefendingWorkers extends UnitGroup
{
	public static boolean stop = false;
	
	private boolean proxyCannon = false;
	
	private boolean workerRush = false;
	
	/**
	 * This unit group manages a group of workers that have been pulled to defend.
	 * @param rejects OutOfJob object to which units are sent that are no longer needed.
	 */
	public DefendingWorkers(OutOfJob rejects) 
	{
		super(rejects);
	}

	@Override
	public void onFrame(Game game, Player self, Tyr bot) 
	{
		cleanup();
		Unit invader = EnemyManager.getManager().getInvader();
		if (EnemyManager.getManager().getInvadingWorkerCount() >= 3)
			workerRush = true;
		
		if (workerRush)
			DebugMessages.addMessage("Worker rush! " + EnemyManager.getManager().getInvadingWorkerCount() + " invading workers.");
		
		int invaderCount = EnemyManager.getManager().getInvaderCount();
		
		DebugMessages.addMessage("Invading enemies: " + invaderCount);
		
		// See whether the enemy has proxied a building.
		boolean proxy = false;
		for (EnemyPosition building : EnemyManager.getManager().enemyBuildingMemory)
		{
			if (building.distanceSq(Tyr.tileToPosition(self.getStartLocation())) <= Settings.getLargeInvasionDist() * Settings.getLargeInvasionDist())
			{
				proxy = true;
				if (building.type == UnitType.Protoss_Photon_Cannon && building.completed)
				{
					proxyCannon = true;
					break;
				}
			}
		}
		
		// If we are no longer being invaded, send the workers back to work.
		if(invader == null 
				|| invader.getDistance(self.getStartLocation().getX()*32, self.getStartLocation().getY()*32) >= 480 || proxyCannon)
		{
			for(Agent agent : units)
				rejects.add(agent);
			if (units.size() != 0)
				units = new ArrayList<Agent>();
			DebugMessages.addMessage("No threat detected.");
			return;
		}
		
		// If we are already well defended we do not need to pull workers to defend.
		if (invader.getDistance(self.getStartLocation().getX()*32, self.getStartLocation().getY()*32) >= 480)
		{
			DebugMessages.addMessage("Invader too far away.");
			return;
		}
		
		// Determine how many workers we want to use to defend.
		int desiredDefenders = 0;
		int halfWorkers = (units.size() + bot.workForce.units.size())/2;
		if(invaderCount == 1 && !invader.getType().isFlyer() && !proxy)
		{
			if(invader.getType().isWorker())
				desiredDefenders = Math.min(1, halfWorkers);
			else
				desiredDefenders = Math.min(3, halfWorkers);
		}
		else if (invaderCount == 2 && !proxy)
			desiredDefenders = Math.min(5, halfWorkers);
		else
			desiredDefenders = halfWorkers;
		
		if (workerRush)
		{
			DebugMessages.addMessage("Worker count: " + (units.size() + bot.workForce.units.size()));
			if (self.getRace() == Race.Terran)
			{
				if (EnemyManager.getManager().getInvadingWorker() == null || EnemyManager.getManager().getInvadingWorker().getDistance(Tyr.tileToPosition(self.getStartLocation())) > 256)
					desiredDefenders = 0;
				else
				{
					int totalWorkers = units.size() + bot.workForce.units.size();
					int keepWorkers = 3;
					if (totalWorkers > 8)
						keepWorkers++;
					desiredDefenders = Math.min(totalWorkers - keepWorkers, invaderCount);
				}
			}
			else
			{
				if (EnemyManager.getManager().getInvadingWorker() == null || EnemyManager.getManager().getInvadingWorker().getDistance(Tyr.tileToPosition(self.getStartLocation())) > 256)
					desiredDefenders = 0;
				else
					desiredDefenders = units.size() + bot.workForce.units.size()-2;
			}

			for (int i = units.size() - 1; i >= 0; i--)
			{
				if (units.get(i).unit.getHitPoints() < 20)
				{
					rejects.add(units.get(i));
					units.remove(i);
				}
			}
		}
		
		if (stop)
			desiredDefenders = 0;
		
		
		// Make sure we have the correct number of workers for the defense.
		while(units.size() < desiredDefenders)
		{
			final Agent newAgent = bot.workForce.pop(invader.getPosition(), workerRush);
			units.add(newAgent);
		}
		while(units.size() > desiredDefenders && units.size() > 0)
		{
			rejects.add(units.get(units.size()-1));
			units.remove(units.size()-1);
		}
		
		for (Agent worker : units)
			if (worker != null && !worker.isDead() && worker.unit.getOrder() != null)
				game.drawTextMap(worker.unit.getPosition().getX(), worker.unit.getPosition().getY(), worker.unit.getOrder().toString());

		DebugMessages.addMessage("Desired defenders: " + desiredDefenders + " Actual defenders: " + units.size());
		
		// Send the workers to attack the invading units.
		if (game.getFrameCount() % 100 == 0 && invader != null)
			for(Agent worker : units)
				if(worker != null)
				{
					if(invaderCount > 1)
						worker.unit.attack(invader.getPosition());
					else
						worker.unit.attack(invader);
				}
	}

}
