/*
* 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 com.tyr.BWTAProxy;
import com.tyr.Settings;
import com.tyr.Tyr;
import com.tyr.agents.Agent;
import com.tyr.agents.DefensiveTank;
import com.tyr.buildingplacement.DefensiveStructures;
import com.tyr.buildingplacement.SpaceManager;

import bwapi.Game;
import bwapi.Player;
import bwapi.Position;
import bwapi.TilePosition;


/**
 * Unit group for putting tanks at a base for defense.
 * @author Simon
 *
 */
public class TankDefense extends UnitGroup 
{
	/**
	 * The target position around which we want to position our tanks.
	 */
	Position target = null;
	
	/**
	 * The number of tanks we will put at each base.
	 */
	public int defensiveTanks = 2;
	
	/**
	 * Unit group for putting tanks at a base for defense.
	 * @param rejects The OutOfJob object to which units are sent that are no longer needed.
	 * @param target The target position around which we want to position our tanks.
	 */
	public TankDefense(OutOfJob rejects, Position target)
	{
		super(rejects);
		this.target = target;
	}
	
	@Override
	public void onFrame(Game game, Player self, Tyr bot)
	{
		// Determine the new target where we will gather the tanks, if necessary.
		if (target == null && bot.defensiveStructures.get(0).defences.size() > 0)
			target = bot.defensiveStructures.get(0).defences.get(0).getPosition();
		else if (target == null)
			target = Settings.getRallyPoint();
		if (target == null)
			target = SpaceManager.getMainExit();
		
		// Give the DefenseiveTank command to all tanks managed by this unit group. 
		for(Agent agent : units)
			if (agent.getCommand().getClass() != DefensiveTank.class)
				agent.order(new DefensiveTank(agent, getPosition(Tyr.positionToTile(target))));
	}
	
	/**
	 * Get a location for the next tank to siege.
	 * @param aroundTile The tile around which we will search for a suitable tank placement.
	 * @return The tile where the tank will be allowed to siege.
	 */
	private Position getPosition(TilePosition aroundTile)
	{
		if (!BWTAProxy.initialized)
			return Tyr.tileToPosition(aroundTile);
		
		// Checks in an ever growing area to see if there is a suitable placement for the tank.
		for(int dist = 0; dist < 10; dist++)
		{
			for (int i=-dist; i <= dist; i++)
			{
				if (checkTile(new TilePosition(aroundTile.getX() + i, aroundTile.getY() - dist)))
					return Tyr.tileToPosition(new TilePosition(aroundTile.getX() + i, aroundTile.getY() - dist));
				if (checkTile(new TilePosition(aroundTile.getX() - dist, aroundTile.getY() + i)))
					return Tyr.tileToPosition(new TilePosition(aroundTile.getX() - dist, aroundTile.getY() + i));
				if (checkTile(new TilePosition(aroundTile.getX() + i, aroundTile.getY() + dist)))
					return Tyr.tileToPosition(new TilePosition(aroundTile.getX() + i, aroundTile.getY() + dist));
				if (checkTile(new TilePosition(aroundTile.getX() + dist, aroundTile.getY() + i)))
					return Tyr.tileToPosition(new TilePosition(aroundTile.getX() + dist, aroundTile.getY() + i));
			}
		}
		return Tyr.tileToPosition(aroundTile);
	}
	
	/**
	 * See if a given tile position is a suitable placement location for a tank.
	 * @param pos The location where the tank is to be placed.
	 * @return Return true if the given tile position is a valid placement for a tank.
	 */
	private boolean checkTile(TilePosition pos) 
	{
		for(DefensiveStructures structures : Tyr.bot.defensiveStructures)
		{
			for(Agent tank : structures.tanks.units)
			{
				if(tank.getCommand().getClass() != DefensiveTank.class)
					continue;
				
				DefensiveTank defensive = (DefensiveTank)tank.getCommand();
				TilePosition defendedTile = Tyr.positionToTile(defensive.target);
				if (Math.abs(defendedTile.getX() - pos.getX()) <= 1 && Math.abs(defendedTile.getY() - pos.getY()) <= 1)
					return false;
			}
		}
		
		for (int dx = -1; dx <= 1; dx++)
			for (int dy = -1; dy <= 1; dy++)
				if (pos.getX() + dx < 0 || pos.getY() + dy < 0 || pos.getX() + dx >= Tyr.game.mapWidth() || pos.getY() + dy >= Tyr.game.mapHeight() 
				|| Tyr.bot.spaceManager.map[pos.getX() + dx][pos.getY() + dy] != 1)
					return false;
		
		return true;
	}

	@Override
	public void add(Agent agent)
	{
		super.add(agent);
	}

	/**
	 * Disables this tank defense, releasing all managed tanks.
	 */
	public void disable()
	{
		defensiveTanks = 0;
		while(units.size() > 0)
		{
			Agent agent = units.get(units.size()-1);
			units.remove(units.size() - 1);
			rejects.add(agent);
		}
	}
}
