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

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

import com.tyr.DebugMessages;
import com.tyr.EnemyManager;
import com.tyr.PositionUtil;
import com.tyr.Settings;
import com.tyr.Tyr;
import com.tyr.UnitTracker;
import com.tyr.agents.Agent;
import com.tyr.agents.Attack;
import com.tyr.buildingplacement.SpaceManager;
import com.tyr.tasks.ClearExpandsTask;
import com.tyr.tasks.ClearMinesTask;
import com.tyr.tasks.ConstantPushSolution;
import com.tyr.tasks.ConstantPushTask;
import com.tyr.tasks.DefendMainTask;
import com.tyr.tasks.DragoonSquadTask;
import com.tyr.tasks.LocalDefenseTask;
import com.tyr.tasks.PokeTask;
import com.tyr.unitgroups.DistributeAttackGroup;

import bwapi.Game;
import bwapi.Player;
import bwapi.Unit;
import bwapi.UnitType;
import bwapi.UpgradeType;
import bwta.BWTA;


public class SafeGateExpand extends CompositeBuildOrder
{
	private ClearMinesTask clearMinesTask;
	private LocalDefenseTask localDefenseTask;
	private LocalDefenseTask naturalDefenseTask;
	private LocalDefenseTask thirdDefenseTask;
	private ConstantPushTask constantPushTask;
	private ConstantPushTask pokeTask;
	private int armySize = 40;
	boolean detectionNeeded;
	boolean attackStarted = false;
	boolean pokeSent = true;
	boolean scoutBuilt = false;
	
	@Override
	public void initialize(Game game, Player self, Tyr bot)
	{
		DefendMainTask.keepZealotsBack = true;
		
		addTask(new DragoonSquadTask());
		
		clearMinesTask = new ClearMinesTask();
		addTask(clearMinesTask);

		localDefenseTask = new LocalDefenseTask(BWTA.getRegion(Tyr.getStartLocation()), 0);
		addTask(localDefenseTask);

		naturalDefenseTask = new LocalDefenseTask(null, 0);
		addTask(naturalDefenseTask);
		thirdDefenseTask = new LocalDefenseTask(null, 0);
		addTask(thirdDefenseTask);
		
		pokeTask = new ConstantPushTask(ConstantPushSolution.DISTRIBUTE);
		
		DistributeAttackGroup.waitDistance = 800;
		
		Settings.setRequiredSize(100);
		Settings.setMaximumSize(100);
		Settings.setMaximumWorkers(7);
		Settings.setWorkersPerGas(2);
		Settings.setLargeInvasionDist(768);
		ExpandPart.maximumCcs = 2;
		ClearExpandsTask.allowFullAssault = false;

		this.add(new WorkerScoutPart(1600));
		this.add(new ExpandPart(true));
		
		Attack.requiredAtCannon = 0;
		
		constantPushTask = new ConstantPushTask(ConstantPushSolution.DISTRIBUTE);
		constantPushTask.stop = true;
		addTask(constantPushTask);
		
		ConstantPushTask.prioritizeExpands = true;
		ConstantPushTask.reinforcementSize = 5;
		
		super.initialize(game, self, bot);
	}

	private static int armySize()
	{
		return completed(ZEALOT) + completed(DRAGOON);
	}

	@Override
	public void onFrame(Game game, Player self, Tyr bot) 
	{
		DebugMessages.addMessage("Army size: " + armySize());
		

		if (completed(SCOUT) > 0)
		{
			scoutBuilt = true;
		}
		
		if (!pokeSent && completed(DRAGOON) >= 12)
		{
			Tyr.bot.taskManager.potentialTasks.add(pokeTask);
			PokeTask.defendingUnitsNeeded = 0;
			PokeTask.required = 10;
			pokeSent = true;
		}
		
		if (pokeSent && pokeTask != null)
		{
			List<Unit> enemyTanks = new ArrayList<Unit>();

			for (Unit enemy : EnemyManager.getEnemyUnits())
				if (Agent.isTank(enemy))
					enemyTanks.add(enemy);

			dragoonLoop : for (Unit unit : self.getUnits())
			{
				if (unit.getType() != DRAGOON && unit.getType() != ZEALOT)
					continue;
				for (Unit enemy : enemyTanks)
				{
					if (PositionUtil.distanceSq(unit, enemy) <= 1000 * 1000)
					{
						pokeTask.remove();
						pokeTask = null;
						break dragoonLoop;
					}
				}
			}
		}
		
		if (completed(NEXUS) > 1
				&& count(ZEALOT) + count(DRAGOON) >= 8)
			detectionNeeded = true;

		if (armySize() >= 23)
		{
			detectionNeeded = true;
		}
		if (armySize() >= 16)
		{
			localDefenseTask.setDefendersNeeded(1);
		}

		if (naturalDefenseTask != null && naturalDefenseTask.getDefendersNeeded() == 0 && Tyr.bot.workForce.mineralWorkers.size() >= 2)
		{
			naturalDefenseTask.setDefendersNeeded(2);
			naturalDefenseTask.setDefendedPosition(Tyr.bot.workForce.mineralWorkers.get(1).resourceDepot.getPosition());
		}

		if (thirdDefenseTask != null && thirdDefenseTask.getDefendersNeeded() == 0 && Tyr.bot.workForce.mineralWorkers.size() >= 3)
		{
			thirdDefenseTask.setDefendersNeeded(3);
			thirdDefenseTask.setDefendedPosition(Tyr.bot.workForce.mineralWorkers.get(2).resourceDepot.getPosition());
		}
		
		super.onFrame(game, self, bot);

		if (gas() >= 400)
			Settings.setWorkersPerGas(2);
		else if ((gas() < 250 && count(GATEWAY) >= 12) 
				|| gas() + 400 < minerals())
			Settings.setWorkersPerGas(3);
		
		if (constantPushTask != null && completed(ZEALOT) + completed(DRAGOON) <= 10)
			constantPushTask.stop = true;
		if (constantPushTask != null && completed(ZEALOT) + completed(DRAGOON) >= armySize)
		{
			attackStarted = true;
			constantPushTask.stop = false;
			ExpandPart.maximumCcs = 3;
		}
		
		if (count(NEXUS) > 1)
			Settings.setMaximumWorkers(40);
		else if (UnitTracker.count(UnitType.Protoss_Cybernetics_Core) > 0)
			Settings.setMaximumWorkers(30);
		else if (UnitTracker.count(UnitType.Protoss_Gateway) >= 4)
			Settings.setMaximumWorkers(25);
		else if (UnitTracker.count(UnitType.Protoss_Gateway) >= 2)
			Settings.setMaximumWorkers(9);
		if (armySize() >= 2 && UnitTracker.count(UnitType.Protoss_Gateway) < 4)
			Settings.setMaximumWorkers(20);
		
		if (UnitTracker.count(UnitType.Protoss_Probe) >= 7 && UnitTracker.count(UnitType.Protoss_Pylon) == 0 && bot.getAvailableMinerals() >= 100)
		{
			bot.spaceManager.build(UnitType.Protoss_Pylon);
		}

		if (SpaceManager.noPower
				&& minerals() >= 100
				&& count(PYLON) == completed(PYLON))
			bot.spaceManager.build(PYLON);
		
		if (self.completedUnitCount(UnitType.Protoss_Pylon) > 0
				&& bot.getAvailableMinerals() >= 125 
				&& UnitTracker.count(UnitType.Protoss_Gateway) == 0
				&& count(NEXUS) > 1)
		{
			bot.spaceManager.build(UnitType.Protoss_Gateway);
		}
			
		if (UnitTracker.count(UnitType.Protoss_Gateway) == 1 
				&& bot.getAvailableMinerals() >= 100
				&& count(CYBERNETICS_CORE) > 0
				&& count(ASSIMILATOR) > 0
				&& count(NEXUS) > 1)
		{
			bot.spaceManager.build(UnitType.Protoss_Gateway);
		}
		
		if (!pauseProduction())
		{
			if (UnitTracker.count(UnitType.Protoss_Gateway) < 4
					&& bot.getAvailableMinerals() >= 300
					&& UnitTracker.count(UnitType.Protoss_Cybernetics_Core) > 0)
			{
				bot.spaceManager.build(UnitType.Protoss_Gateway);
			}
			
			if (UnitTracker.count(UnitType.Protoss_Gateway) >= 3
					&& UnitTracker.count(UnitType.Protoss_Gateway) < 7
					&& bot.getAvailableMinerals() >= 300
					&& count(ZEALOT) + count(DRAGOON) >= 7)
			{
				bot.spaceManager.build(UnitType.Protoss_Gateway);
			}
			
			if (UnitTracker.count(UnitType.Protoss_Gateway) >= 7
					&& UnitTracker.count(UnitType.Protoss_Gateway) < 12
					&& bot.getAvailableMinerals() >= 300
					&& count(ZEALOT) + count(DRAGOON) >= 7)
			{
				bot.spaceManager.build(UnitType.Protoss_Gateway);
			}
		}
		
		//if we're running out of supply and have enough minerals ...
		if ((self.supplyTotal() + UnitTracker.getSupplyConstructing() - self.supplyUsed() 
					<= UnitTracker.count(UnitType.Protoss_Gateway)*3 + UnitTracker.getCcCount() * 3)
				&& (bot.getAvailableMinerals() >= 100)
				&& self.supplyTotal() + UnitTracker.getSupplyConstructing() < 400
				&& UnitTracker.count(UnitType.Protoss_Pylon) >= 1
				&& (armySize() >= 2 || self.supplyTotal() + UnitTracker.getSupplyConstructing() - self.supplyUsed() < 4))
		{
			bot.spaceManager.build(UnitType.Protoss_Pylon);
		}
		
		if(UnitTracker.count(UnitType.Protoss_Assimilator) < 2
				&& bot.getAvailableMinerals() >= 100 
				&& UnitTracker.getGeyserCount() > 0
				&& (count(CYBERNETICS_CORE) > 0 || minerals() >= 300)
				&& count(NEXUS) >= 2)
		{
			bot.spaceManager.build(UnitType.Protoss_Assimilator);
		}
		
		if (bot.getAvailableMinerals() >= 200 
				&& UnitTracker.count(UnitType.Protoss_Cybernetics_Core) < 1
				&& completed(GATEWAY) > 0)
		{
			bot.spaceManager.build(UnitType.Protoss_Cybernetics_Core);
		}
		
		if (bot.getAvailableMinerals() >= 150
				&& UnitTracker.count(UnitType.Protoss_Dragoon) + UnitTracker.count(UnitType.Protoss_Zealot) >= 15
				&& UnitTracker.count(UnitType.Protoss_Forge) == 0)
		{
			bot.spaceManager.build(UnitType.Protoss_Forge);
		}
		
		if (bot.getAvailableMinerals() >= 300
				&& UnitTracker.count(UnitType.Protoss_Nexus) >= 2
				&& UnitTracker.count(UnitType.Protoss_Cybernetics_Core) > 0
				&& UnitTracker.count(UnitType.Protoss_Forge) < 2)
		{
			bot.spaceManager.build(UnitType.Protoss_Forge);
		}
		
		if (count(SHUTTLE) > 0
				&& bot.getAvailableMinerals() >= 150
				&& bot.getAvailableGas() >= 100
				&& UnitTracker.count(UnitType.Protoss_Citadel_of_Adun) == 0
				&& self.completedUnitCount(UnitType.Protoss_Cybernetics_Core) > 0
				&& UnitTracker.count(UnitType.Protoss_Gateway) >= 4
				&& count(ZEALOT) + count(DRAGOON) >= 20)
		{
			bot.spaceManager.build(UnitType.Protoss_Citadel_of_Adun);
		}
		
		if (gas() >= 200 && minerals() >= 200
				&& (count(GATEWAY) >= 7 || detectionNeeded)
				&& count(ROBOTICS_FACILITY) < 2
				&& count(GATEWAY) >= 2)
		{
			bot.spaceManager.build(ROBOTICS_FACILITY);
		}
		
		if (gas() >= 50 && minerals() >= 100
				&& completed(ROBOTICS_FACILITY) > 0
				&& count(OBSERVATORY) == 0)
		{
			bot.spaceManager.build(OBSERVATORY);
		}
	}
	
	private boolean pauseProduction()
	{
		if (gas() > 250 && minerals() > 400)
			return false;

		if (detectionNeeded
				&& count(DRAGOON) >= 2
				&& count(GATEWAY) > 2
				&& completed(CYBERNETICS_CORE) > 0
				&& count(ROBOTICS_FACILITY) == 0)
			return true;
		if (detectionNeeded
				&& armySize() >= 2
				&& completed(CYBERNETICS_CORE) > 0
				&& completed(ROBOTICS_FACILITY) == 1
				&& count(OBSERVATORY) == 0)
			return true;
		if (detectionNeeded
				&& armySize() >= 2
				&& completed(CYBERNETICS_CORE) > 0
				&& completed(OBSERVATORY) == 1
				&& count(OBSERVER) < 3
				&& completed(OBSERVER) == count(OBSERVER))
			return true;
		
		if (completed(STARGATE) > 0
				&& count(SCOUT) == 0)
			return true;
		
		if (UnitTracker.count(CITADEL) == 0
				&& completed(CYBERNETICS_CORE) > 0
				&& count(GATEWAY) >= 4
				&& count(ZEALOT) + count(DRAGOON) >= 20)
			return true;
		
		if (minerals() < 400
				&& count(NEXUS) <= 2
				&& count(NEXUS) < ExpandPart.maximumCcs)
			return true;
		
		if (minerals() >= 300)
			return false;
		if (completed(OBSERVER) >= 3
				&& completed(ROBOTICS_FACILITY) > 0
				&& count(SHUTTLE) < 2
				&& count(SHUTTLE) == completed(SHUTTLE))
			return true;
		if (count(ZEALOT) >= 2 && count(CYBERNETICS_CORE) == 0)
			return true;
		if (count(ZEALOT) >= 2 && count(ASSIMILATOR) == 0)
			return true;
		return false;
	}
	
	@Override
	public boolean overrideStructureOrder(Game game, Player self, Tyr bot, Agent agent)
	{
		if (agent.unit.getType() == UnitType.Protoss_Gateway && !agent.unit.isTraining())
		{
			if (count(DRAGOON) <= 10
					&& !pauseProduction())
			{
				if (bot.getAvailableMinerals() >= 125 && bot.getAvailableGas() >= 50)
					agent.unit.build(DRAGOON);
				return true;
			}
			if ((!pauseProduction() || minerals() >= 300)
					&& count(ZEALOT) + 3 <= count(DRAGOON)
					&& count(DRAGOON) >= 10
					&& bot.getAvailableMinerals() >= 100)
				agent.unit.build(ZEALOT);
			else if (!pauseProduction()
					&& (count(ZEALOT)  + 3 > count(DRAGOON))
					&& bot.getAvailableMinerals() >= 125 && bot.getAvailableGas() >= 50)
				agent.unit.build(DRAGOON);
			return true;
		}
		else if (agent.unit.getType() == UnitType.Protoss_Cybernetics_Core && !agent.unit.isUpgrading())
		{
			if(bot.getAvailableMinerals() >= UpgradeType.Singularity_Charge.mineralPrice()
					&& count(DRAGOON) >= 3
					&& bot.getAvailableGas() >= UpgradeType.Singularity_Charge.gasPrice())
				agent.unit.upgrade(UpgradeType.Singularity_Charge);
		}
		else if (agent.unit.getType() == UnitType.Protoss_Forge && !agent.unit.isUpgrading())
		{
			if(bot.getAvailableMinerals() >= UpgradeType.Protoss_Ground_Weapons.mineralPrice()
					&& bot.getAvailableGas() >= UpgradeType.Protoss_Ground_Weapons.gasPrice())
				agent.unit.upgrade(UpgradeType.Protoss_Ground_Weapons);
			
			if(bot.getAvailableMinerals() >= UpgradeType.Protoss_Ground_Armor.mineralPrice()
					&& bot.getAvailableGas() >= UpgradeType.Protoss_Ground_Armor.gasPrice())
				agent.unit.upgrade(UpgradeType.Protoss_Ground_Armor);
		}
		else if (agent.unit.getType() == CITADEL && !agent.unit.isUpgrading())
		{
			if (gas() >= UpgradeType.Leg_Enhancements.gasPrice()
					&& minerals() >= UpgradeType.Leg_Enhancements.mineralPrice())
				agent.unit.upgrade(UpgradeType.Leg_Enhancements);
		}
		else if (agent.unit.getType() == ROBOTICS_FACILITY)
		{
			if ((count(OBSERVER) < 3)
					&& minerals() >= 25
					&& gas() >= 75
					&& !agent.unit.isTraining())
				agent.unit.build(OBSERVER);
			if (minerals() >= 200
					&& !agent.unit.isTraining()
					&& count(SHUTTLE) < 2)
				agent.unit.build(SHUTTLE);
			return true;
		}
		else if (agent.unit.getType() == STARGATE)
		{
			if (count(SCOUT) == 0 && !agent.unit.isTraining())
				agent.unit.train(SCOUT);
			return true;
		}
		return false;
	}
}
