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

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

import com.tyr.buildingplacement.BuildCommand;
import com.tyr.unitgroups.MineralWorkers;

import bwapi.Color;
import bwapi.Unit;
import bwapi.UnitType;

/**
 * This class tracks our own units, to see how many of each type we have.
 * @author Simon
 *
 */
public class UnitTracker 
{
	/**
	 * Keeps track of how many units of each unit type the player has.
	 */
	private static HashMap<UnitType, Integer> unitCounts = new HashMap<UnitType, Integer>();
	
	/**
	 * Keeps track of how many addOns of each unit type are connected to a building.
	 */
	private static HashMap<UnitType, Integer> usableAddOns = new HashMap<UnitType, Integer>();
	
	/**
	 * The number of gas geysers we are able to take.
	 */
	private static int geyserCount;
	
	/**
	 * The number of main bases we have.
	 */
	private static int ccCount;
	
	/**
	 * The amount of supply we get from buildings that are being constructed.
	 */
	private static int supplyConstructing;
	
	/**
	 * List of all known neutral structures (e.g. mineral patches, destructible buildings).
	 */
	private static ArrayList<EnemyPosition> neutralStructures;
	
	/**
	 * The last frame the counts of unit was updated.
	 */
	private static int updatedFrame = -1;
	
	/**
	 * Returns the number of units we have of a specific type.
	 * @param type The type of unit of which we want to know how many we have.
	 * @return The number of units we have of the specific type.
	 */
	public static int count(UnitType type)
	{
		update();
		if (!unitCounts.containsKey(type))
			return 0;
		return unitCounts.get(type);
	}
	
	/**
	 * Returns the number of units we have of a specific type of add on.
	 * Only add ons connected to a building are counted.
	 * @param type The type of add on of which we want to know how many we have.
	 * @return The number of units we have of a specific type of add on.
	 */
	public static int usableAddOns(UnitType type)
	{
		update();
		if (!usableAddOns.containsKey(type))
			return 0;
		return usableAddOns.get(type);
	}
	
	/**
	 * The number of main bases we have.
	 * @return The number of main bases we have.
	 */
	public static int getCcCount()
	{
		update();
		return ccCount;
	}
	
	/**
	 * The number of gas geysers we are able to take.
	 * @return The number of gas geysers we are able to take.
	 */
	public static int getGeyserCount()
	{
		update();
		return geyserCount;
	}
	
	/**
	 * The amount of supply we get from buildings that are being constructed.
	 * @return The amount of supply we get from buildings that are being constructed.
	 */
	public static int getSupplyConstructing()
	{
		update();
		return supplyConstructing;
	}
	
	public static ArrayList<EnemyPosition> getNeutralStructures()
	{
		return neutralStructures;
	}
	
	/**
	 * Updates the unit counts if necessary.
	 */
	private static void update()
	{
		if (Tyr.game.getFrameCount() == updatedFrame)
			return;
		updatedFrame = Tyr.game.getFrameCount();
		
		
		ForceTracker.update();
		for (Force f : ForceTracker.forces)
			if (!f.disabled())
				Tyr.drawCircle(f.getCenter(), Color.Red, 16);
		
		if (neutralStructures == null)
		{
			neutralStructures = new ArrayList<EnemyPosition>();
			
			for(Unit unit : Tyr.game.getNeutralUnits())
				neutralStructures.add(new EnemyPosition(unit.getID(), unit.getType(), unit.getPosition(), true));
		}
		
		// Reset the numbers.
		unitCounts = new HashMap<UnitType, Integer>();
		usableAddOns = new HashMap<UnitType, Integer>();
		ccCount = 0;
		supplyConstructing = 0;
		geyserCount = 0;
		
		List<Unit> myUnits = Tyr.self.getUnits();
		
		// Count the number of vespene geysers that we have not yet taken that are near our bases.
		for (MineralWorkers base : Tyr.bot.workForce.mineralWorkers)
			if (base.gasWorkers != null && !base.resourceDepot.isBeingConstructed() && base.gasWorkers.geyser.getType() == UnitType.Resource_Vespene_Geyser)
				geyserCount++;
		
		// Subtract the number of geysers for which we have a buildCommand.
		for(BuildCommand command : Tyr.bot.buildCommands)
			if (command.building.isRefinery())
				geyserCount--;
		
		if (myUnits == null)
		{
			DebugMessages.addMessage("Whoops, getUnits is null!");
		}
		else
		{
			// Add our existing units to the map.
			for (Unit myUnit : myUnits) 
			{
				if(!unitCounts.containsKey(myUnit.getType()))
					unitCounts.put(myUnit.getType(), 1);
				else
					unitCounts.put(myUnit.getType(), unitCounts.get(myUnit.getType())+1);
				if (myUnit.getType().isResourceDepot())
					ccCount++;
				if (myUnit.isBeingConstructed() && !myUnit.getType().isResourceDepot())
					supplyConstructing += myUnit.getType().supplyProvided();
				if (myUnit.isMorphing() && myUnit.getBuildType() == UnitType.Zerg_Overlord)
					supplyConstructing += UnitType.Zerg_Overlord.supplyProvided();
				Unit addOn = myUnit.getAddon(); 
				if (addOn != null)
				{
					if (!usableAddOns.containsKey(addOn.getType()))
						usableAddOns.put(addOn.getType(), 1);
					else
						usableAddOns.put(addOn.getType(), usableAddOns.get(addOn.getType())+1);
				}
			}
		}
		
		// Count all units from the build commands.
		for(BuildCommand command : Tyr.bot.buildCommands)
		{
			if(!unitCounts.containsKey(command.building))
				unitCounts.put(command.building, 1);
			else
				unitCounts.put(command.building, unitCounts.get(command.building)+1);
			
			if (command.building.isResourceDepot())
				ccCount++;
			
			if (!command.building.isResourceDepot())
				supplyConstructing += command.building.supplyProvided();
		}
		
		// Add unit counts from the walloff.
		if (Tyr.bot.wallOff != null)
		{
			for(BuildCommand command : Tyr.bot.wallOff.underConstruction)
			{
				if(!unitCounts.containsKey(command.building))
					unitCounts.put(command.building, 1);
				else
					unitCounts.put(command.building, unitCounts.get(command.building)+1);
				
				if (command.building.isResourceDepot())
					ccCount++;
				
				if (!command.building.isResourceDepot())
					supplyConstructing += command.building.supplyProvided();
			}
		}
	}
	
	/**
	 * Count the number of siege tanks.
	 * @return Count the number of siege tanks.
	 */
	public static int countTanks()
	{
		return count(UnitType.Terran_Siege_Tank_Siege_Mode) + count(UnitType.Terran_Siege_Tank_Tank_Mode);
	}
	
	/**
	 * Increment the number of addOns of a specific type that has been built.
	 * @param addOn The type of add on that has been built.
	 */
	public static void addAddOn(UnitType addOn)
	{
		if (!usableAddOns.containsKey(addOn))
			usableAddOns.put(addOn, 0);
		usableAddOns.put(addOn, usableAddOns.get(addOn) + 1);
	}
}