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

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import com.tyr.BWTAProxy;
import com.tyr.EnemyManager;
import com.tyr.EnemyPosition;
import com.tyr.PositionUtil;
import com.tyr.Tyr;
import com.tyr.UnitTracker;
import com.tyr.agents.Agent;

import bwapi.Position;
import bwapi.TilePosition;
import bwapi.Unit;
import bwapi.UnitType;
import bwta.BaseLocation;

/**
 * Build site locator for finding a new base location.
 * @author Simon
 *
 */
public class BaseBuildSite extends BuildSiteLocator 
{
	/**
	 * Do we look for a base which has a gas geyser? 
	 */
	private boolean gasOnly;
	
	/**
	 * Build site locator for finding a new base location.
	 * @param gasOnly Do we look for a base which has a gas geyser?
	 */
	public BaseBuildSite(boolean gasOnly)
	{
		this.gasOnly = gasOnly;
	}
	
	@Override
	public TilePosition findPlacement(UnitType building, TilePosition preferredTile, Agent worker)
	{
		// Find a suitable tile position for placing the building.
		if(preferredTile == null)
		{
			if(Tyr.game.isVisible(Tyr.self.getStartLocation()))
				preferredTile = Tyr.self.getStartLocation();
			else
			{
				if(Tyr.bot.workForce.mineralWorkers.size() > 0)
					preferredTile = Tyr.bot.workForce.mineralWorkers.get(0).resourceDepot.getTilePosition();
				else
					preferredTile = worker.unit.getTilePosition();
			}
		}
		return getBuildTile(worker.unit, building, preferredTile);
	}
	
    /**
     * Gets a suitable build poition for the building.
     * @param builder The worker who will build the building.
     * @param buildingType The type of building to be built.
     * @param aroundTile The tile position of our main base.
     * @return The TilePosition where the building can be built.
     */
	public TilePosition getBuildTile(Unit builder, UnitType buildingType, TilePosition aroundTile) 
	{
	 	if (!buildingType.isResourceDepot())
	 		return null;

	 	BaseLocation loc = null;
	 	double bestDist = Integer.MAX_VALUE;
	 	double enemyDist = 0;
	 	if (Tyr.game.mapFileName().contains("Alchemist") && UnitTracker.getCcCount() <= 1 && Tyr.bot.suspectedEnemy.size() == 1)
	 	{
			final Position enemyMain = Tyr.bot.suspectedEnemy.get(0).getPosition();
			final Position main = Tyr.getStartLocation();
			
			List<TilePosition> bases = new ArrayList<>();
			for (BaseLocation base : Tyr.bot.expands)
				if (PositionUtil.distanceSq(base.getPosition(), main) >= 100 * 100)
					bases.add(base.getTilePosition());
			bases.sort(new Comparator<TilePosition>()
			{
				@Override
				public int compare(TilePosition p1, TilePosition p2)
				{
					return PositionUtil.distanceSq(main, Tyr.tileToPosition(p1)) - PositionUtil.distanceSq(main, Tyr.tileToPosition(p2));
				}
			});
			
			if (PositionUtil.distanceSq(Tyr.tileToPosition(bases.get(0)), enemyMain) <= PositionUtil.distanceSq(Tyr.tileToPosition(bases.get(1)), enemyMain))
				return bases.get(0);
			else
				return bases.get(1);
	 	}
	 	for (BaseLocation b : Tyr.bot.expands)
	 	{
	 		// Do not take this base if we are looking for a gas base and this has no gas.
	 		if (gasOnly && b.isMineralOnly())
	 			continue;
	 		
	 		// We do not try to take an island expand.
	 		if (b.isIsland())
	 			continue;
	 		
	 		// Do not take bases with very little resources.
	 		if (b.minerals() < 8000)
	 			continue;
	 		
	 		// If we cannot build at this expand, we try the next one.
	 		if (!Tyr.game.canBuildHere(b.getTilePosition(), buildingType, builder, false))
	 		{
	 			continue;
	 		}
	 		
	 		// See if the enemy has already taken this base.
	 		boolean enemyBase = false;
	 		for(EnemyPosition p : EnemyManager.getManager().enemyBuildingMemory)
	 		{
	 			enemyBase = b.getPosition().getDistance(p.pos.getX(), p.pos.getY()) < 256;
	 			if(enemyBase)
	 				break;
	 		}
	 		
	 		if (enemyBase)
	 			continue;
	 		
	 		if (enemyDist > 0
	 				&& PositionUtil.distanceSq(b.getPosition(), Tyr.bot.suspectedEnemy.get(0).getPosition()) > enemyDist)
	 			continue;
	 		
			// We want the closest base to our own.
	 		if (BWTAProxy.initialized)
			{
				final double dist = dist(b.getTilePosition(), aroundTile);
				if (dist >= 0)
				{
					if (PositionUtil.distanceSq(b.getPosition(), Tyr.getStartLocation()) <= 400 * 400)
						bestDist += 1000000000.0;
			 		if (loc == null || dist < bestDist)
			 		{
			 			loc = b;
			 			bestDist = dist;
			 		}
				}
			}
			else if (loc == null || aroundTile.getDistance(b.getTilePosition()) < aroundTile.getDistance(loc.getTilePosition()))
		 		loc = b;
	 	}
 		return loc==null?null:loc.getTilePosition();
 	}
	
	private double dist(TilePosition pos, TilePosition aroundTile)
	{
		if (Tyr.game.mapFileName().contains("Plasma"))
			return PositionUtil.distanceSq(Tyr.tileToPosition(pos), Tyr.tileToPosition(aroundTile));
		else return BWTAProxy.getGroundDistance(aroundTile, pos);
	}
}
