/*
* 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.EnemyManager;
import com.tyr.Tyr;
import com.tyr.agents.Agent;

import bwapi.Game;
import bwapi.Player;
import bwapi.UnitType;


/**
 * This unit group manages all bunkers and the units supporting those bunkers.
 * @author Simon
 *
 */
public class Bunkers extends UnitGroup
{
	/**
	 * List of bunker crews that each manage a single bunker.
	 */
	public ArrayList<BunkerCrew> bunkers = new ArrayList<BunkerCrew>();
	
	/**
	 * Are all bunkers disabled?
	 */
	public boolean disabled;

	/**
	 * The number of scvs per bunker which will always stand ready to repair the bunker.
	 */
	public int minimumRepair = 1;
	
	/**
	 * The number of scvs per bunker that will be used when the enemy is attacking. 
	 */
	public int maximumRepair = 3;
	
	/**
	 * This unit group manages all bunkers and the units supporting those bunkers.
	 * @param rejects The OutOfJob object to which units are sent that are no longer needed.
	 */
	public Bunkers(OutOfJob rejects)
	{
		super(rejects);
	}

	@Override
	public void onFrame(Game game, Player self, Tyr bot)
	{
		if (disabled)
		{
			// If the bunkers are disabled, we send all repairing scvs back to work and we unload all bunkers.
			for(int i=bunkers.size()-1; i>=0; i--)
			{
				BunkerCrew crew = bunkers.get(i);
				while (crew.repairingSCVCount() > 0)
					rejects.add(crew.removeRepairSCV());
				
				crew.bunker.unit.unloadAll();
				
				for (int j=crew.defenders.size()-1; j>=0; j--)
				{
					Agent defender = crew.defenders.get(j);
					rejects.add(defender);
					crew.defenders.remove(j);
				}
			}
			return;
		}
		
		// Determine how many scvs are needed to repair each bunker.
		int requiredRepair = (EnemyManager.getManager().getInvader() == null || 
				(EnemyManager.getManager().getInvaderCount() == 1 && EnemyManager.getManager().getInvader().getType().isWorker()))?
						minimumRepair
						:maximumRepair;
		
		
		ArrayList<BunkerCrew> destroyedBunkers = new ArrayList<BunkerCrew>();
		for(BunkerCrew crew : bunkers)
		{
			// If the bunker has been destroyed, remove it from the list of managed bunkers.
			if (crew.bunker == null && crew.defenders.size() == 0 && crew.repairingSCVCount() == 0)
			{
				destroyedBunkers.add(crew);
				continue;
			}
			
			// Get the units necessary to man the bunker.
			while(crew.defenders.size() < 4)
			{
				Agent def = null;
				
				// Try to get an available unit to man our bunker.
				if (bot.homeGroup.units.size() > 0)
					def = bot.homeGroup.pop(UnitType.Terran_Marine);
				
				// If we didn't find any units we won't be able to fill the bunker.
				if(def == null)
					break;
				
				// We may have gotten a wrong unit type, in which case we don't try to put it in the bunker.
				if(def.unit.getType() != UnitType.Terran_Marine)
					rejects.add(def);
				else
					crew.addDefender(def);	// If we find a unit, add it.
			}
			
			// If we do not yet have enough scvs to repair, get more.
			while(crew.repairingSCVCount() < requiredRepair)
			{
				Agent scv = bot.workForce.pop(crew.bunker.unit.getPosition());
				if(scv == null)
					return;
				crew.addRepairSCV(scv);
			}
			
			// If we have too many scvs repairing, send some back.
			while(crew.repairingSCVCount() > requiredRepair)
				rejects.add(crew.removeRepairSCV());
			
			crew.onFrame(game, self, bot);
		}
		
		for(BunkerCrew crew : destroyedBunkers)
			bunkers.remove(crew);
	}

	@Override
	public void add(Agent agent)
	{
		super.add(agent);
		BunkerCrew crew = new BunkerCrew(rejects);
		crew.bunker = agent;
		bunkers.add(crew);
	}
	
	/**
	 * Does there exist a bunker with at least four units inside?
	 * @return Does there exist a bunker with at least four units inside?
	 */
	public boolean mannedBunkerExists()
	{
		for (BunkerCrew bunker : bunkers)
		{
			if (bunker.defenders.size() >= 4)
				return true;
		}
		return false;
	}

	/**
	 * Are there four units in every bunker?
	 * @return Are there four units in every bunker?
	 */
	public boolean bunkersAreManned() 
	{
		for (BunkerCrew bunker : bunkers)
		{
			if (bunker.defenders.size() < 4)
				return false;
		}
		return true;
	}
	
	@Override
	public void cleanup()
	{
		super.cleanup();
		for(BunkerCrew bunker : bunkers)
			bunker.cleanup();
	}
	
	@Override
	public int takeAgent(Agent agent)
	{
		if(agent.unit.getType() == UnitType.Terran_Bunker && agent.unit.isCompleted())
			return 10;
		else
			return -1;
	}
}
