#include "MicroTanks.h"

#include "BWTA.h"
#include "The.h"
#include "UnitUtil.h"

using namespace UAlbertaBot;

MicroTanks::MicroTanks() 
{ 
}

void MicroTanks::executeMicro(const BWAPI::Unitset & targets, const UnitCluster & cluster)
{
	const BWAPI::Unitset tanks = Intersection(getUnits(), cluster.units);
	if (tanks.empty())
	{
		return;
	}

	// figure out targets
	BWAPI::Unitset tankTargets;
    std::copy_if(targets.begin(), targets.end(), std::inserter(tankTargets, tankTargets.end()), 
                 [](BWAPI::Unit u){ return u->isVisible() && !u->isFlying(); });
    
    const int siegeTankRange = BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode.groundWeapon().maxRange() - 8;

	for (const auto tank : tanks)
	{
        bool tankNearChokepoint = false; 
        for (auto & choke : BWTA::getChokepoints())
        {
            if (choke->getCenter().getDistance(tank->getPosition()) < 64)
            {
                tankNearChokepoint = true;
                break;
            }
        }
		//防守路口的坦克一直架起
		if (order.getType() == SquadOrderTypes::Defend)
		{
			BWAPI::Unit target = getTarget(tank, tankTargets);

			//保持在防守位置架起,攻击范围内的敌军
			if (tank->getDistance(order.getPosition()) <= 60 || tank->getDistance(target) <= siegeTankRange + 20)
			{				
				if (tank->canSiege())
				{
					the.micro.Siege(tank);
				}
				
				if (!tankTargets.empty())
				{
					//发现目标就进攻
					if (target && tank->getDistance(target) <= siegeTankRange && tank->isSieged())
					{
						the.micro.AttackUnit(tank, target);
					}
				}
			}
			//移动到防守位置
			else if (tank->getDistance(order.getPosition()) > 60 && tank->getDistance(target) > siegeTankRange + 100)
			{
				/*
				if (!tankTargets.empty())
				{
					BWAPI::Unit target = getTarget(tank, tankTargets);
					if (target && tank->getDistance(target) > siegeTankRange )
					{
						the.micro.AttackMove(tank, order.getPosition());
					}
					else if (tank->canSiege())
					{
						the.micro.Siege(tank);
					}
					else if (target)
					{
						the.micro.AttackUnit(tank, target);
					}
				}
				else
				{
					if (tank->canUnsiege())
					{
						tank->unsiege();
					}
					// move to it
					the.micro.AttackMove(tank, order.getPosition());
				}
				*/
				if (tank->canUnsiege())
				{
					tank->unsiege();
				}
				the.micro.AttackMove(tank, order.getPosition());
			}
		}
		/*
		//推进阵线上的坦克按照步步为营的方式推进
		else if (order.getType() == SquadOrderTypes::ForwardLine)
		{
			if (tank->getDistance(order.getPosition()) <= 100)
			{
				if (tank->canSiege() && !tank->isMoving())
				{
					the.micro.Siege(tank);
				}
				if (!tankTargets.empty())
				{
					BWAPI::Unit target = getTarget(tank, tankTargets);
					if (target)
					{
						the.micro.AttackUnit(tank, target);
					}
				}
			}
			//附近没有目标的时候直接移动到指定位置
			else if (tank->getDistance(order.getPosition()) > 100 && tankTargets.empty())
			{
				if (tank->canUnsiege())
				{
					tank->unsiege();
				}
				// move to it
				the.micro.AttackMove(tank, order.getPosition());
			}
			//附近有目标则选择稳步推进
			else if (tank->getDistance(order.getPosition()) > 100 && !tankTargets.empty())
			{
				if (tank->canSiege() && !tank->isMoving())
				{
					the.micro.Siege(tank);
				}
				// forward line
				else 
				{
					TankForwardLine(tank, order.getPosition(), tankTargets);
				}
			}
		}
		*/
		else if (order.isCombatOrder())
		{
			if (order.getType() == SquadOrderTypes::ForwardLine)
			{
				if (tank->getDistance(order.getPosition()) < 100)
				{
					if (tank->canSiege())
					{
						the.micro.Siege(tank);
					}
				}
			}

			if (!tankTargets.empty())
			{
				BWAPI::Unit target = getTarget(tank, tankTargets);

				if (target && Config::Debug::DrawUnitTargetInfo)
				{
					BWAPI::Broodwar->drawLineMap(tank->getPosition(), tank->getTargetPosition(), BWAPI::Colors::Purple);
				}

				bool shouldSiege = tank->canSiege() && !tankNearChokepoint;

				if (target)
				{
					// Don't siege to fight buildings, unless they can shoot back.
					if (target->getType().isBuilding() &&
						target->getType().groundWeapon() == BWAPI::WeaponTypes::None &&
						target->getType() != BWAPI::UnitTypes::Terran_Bunker)
					{
						shouldSiege = false;
					}

					// Also don't siege for spider mines.
					else if (target->getType() == BWAPI::UnitTypes::Terran_Vulture_Spider_Mine)
					{
						shouldSiege = false;
					}

					// Also don't siege for single enemy units with low hitpoints,
					// or for single melee units (or units that can't attack us).
					else if (tankTargets.size() == 1)
					{
						if (target->getHitPoints() + target->getShields() <= 60 || UnitUtil::GetAttackRange(target, tank))
						{
							shouldSiege = false;
						}
					}
				}
				//target && tank->getDistance(target) < 64 ||
				bool shouldUnsiege =
										// target is too close
					target && tank->getDistance(target) > siegeTankRange ||		// target is too far away
					tank->isUnderDisruptionWeb();

				if (target &&
					tank->getDistance(target) < siegeTankRange &&
					shouldSiege &&
					!shouldUnsiege &&
					tank->canSiege())
                {
					the.micro.Siege(tank);
				}
				else if (tank->canUnsiege() && (!target || shouldUnsiege))
                {
					the.micro.Unsiege(tank);
                }

				if (target)
				{
					if (tank->isSieged())
					{
						the.micro.AttackUnit(tank, target);
					}
					else
					{
						the.micro.KiteTarget(tank, target);
					}
				}
 			}
			// There are no targets.
			else
			{
				// if we're not near the order position
				if (tank->getDistance(order.getPosition()) > 100)
				{
                    if (tank->canUnsiege())
                    {
                        tank->unsiege();
                    }
                    else
                    {
    					// move to it
    					the.micro.AttackMove(tank, order.getPosition());
                    }
				}
			}
		}
	}
}
///////////////////////////////////////++
//为坦克写一个专门的推进函数，同一时间只让队列中的4个坦克向前推进，剩下的在原地架起，等待前面4个坦克架起后，阵线后面的坦克再向前推进
void MicroTanks::TankForwardLine(BWAPI::Unit attacker, const BWAPI::Position & targetPosition, BWAPI::Unitset & tankTargets)
{
	if (!attacker || !attacker->exists() || attacker->getPlayer() != BWAPI::Broodwar->self() || !targetPosition.isValid())
	{
		UAB_ASSERT(false, "bad arg");
		return;
	}
	// if we have issued a command to this unit already this frame, ignore this one
	if (attacker->getLastCommandFrame() >= BWAPI::Broodwar->getFrameCount() || attacker->isAttackFrame())
	{
		return;
	}

	// get the unit's current command
	BWAPI::UnitCommand currentCommand(attacker->getLastCommand());

	// if we've already told this unit to attack this target, ignore this command
	if (currentCommand.getType() == BWAPI::UnitCommandTypes::Attack_Move &&	currentCommand.getTargetPosition() == targetPosition)
	{
		return;
	}

	int numTankMode = 0;

	for (const auto unit : attacker->getUnitsInRadius(200))
	{
		if (unit->getPlayer() == BWAPI::Broodwar->self() && unit->getType() == BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode)
		{
			numTankMode += 1;
		}
		if (numTankMode >= 4){ break; }
	}
	if (numTankMode >= 3)
	{
		if (attacker->canSiege())
		{
			the.micro.Siege(attacker);
		}

		if (!tankTargets.empty())
		{
			BWAPI::Unit target = getTarget(attacker, tankTargets);
			//发现目标就进攻
			if (target)
			{
				the.micro.AttackUnit(attacker, target);
			}
		}
	}
	else if (numTankMode < 3)
	{
		BWAPI::TilePosition start = attacker->getTilePosition();
		BWAPI::TilePosition end = BWAPI::TilePosition(targetPosition);
		const std::vector<BWAPI::TilePosition> & tilePositions = BWTA::getShortestPath(start, end);
		if (attacker->canUnsiege())
		{
			attacker->unsiege();
		}
		the.micro.Move(attacker, BWAPI::Position(tilePositions[3]));
	}
}
///////////////////////////////////////__

BWAPI::Unit MicroTanks::getTarget(BWAPI::Unit tank, const BWAPI::Unitset & targets)
{
    int highPriority = 0;
	int closestDist = 99999;
	BWAPI::Unit closestTarget = nullptr;

    const int siegeTankRange = BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode.groundWeapon().maxRange() - 8;
    BWAPI::Unitset targetsInSiegeRange;
    for (const auto target : targets)
    {
        if (target->getDistance(tank) < siegeTankRange && !target->isFlying())
        {
            targetsInSiegeRange.insert(target);
        }
    }

    const BWAPI::Unitset & newTargets = targetsInSiegeRange.empty() ? targets : targetsInSiegeRange;

    // check first for units that are in range of our attack that can cause damage
    // choose the highest priority one from them at the lowest health
    for (const auto target : newTargets)
    {
        int distance = tank->getDistance(target);
        int priority = getAttackPriority(tank, target);

		if (!closestTarget || (priority > highPriority) || (priority == highPriority && distance < closestDist))
		{
			closestDist = distance;
			highPriority = priority;
			closestTarget = target;
		}       
    }

    return closestTarget;
}

// Only targets that the tank can potentially attack go into the target set.
int MicroTanks::getAttackPriority(BWAPI::Unit tank, BWAPI::Unit target)
{
	BWAPI::UnitType targetType = target->getType();

	if (target->getType() == BWAPI::UnitTypes::Zerg_Larva || target->getType() == BWAPI::UnitTypes::Zerg_Egg)
	{
		return 0;
	}

	// If it's under dark swarm, we can't hurt it unless we're sieged.
	if (target->isUnderDarkSwarm() && !tank->isSieged())
	{
		return 0;
	}

	// A ghost which is nuking is the highest priority by a mile.
	if (targetType == BWAPI::UnitTypes::Terran_Ghost &&
		target->getOrder() == BWAPI::Orders::NukePaint ||
		target->getOrder() == BWAPI::Orders::NukeTrack)
	{
		return 15;
	}

	// if the target is building something near our base something is fishy
	BWAPI::Position ourBasePosition = BWAPI::Position(BWAPI::Broodwar->self()->getStartLocation());
	if (target->getType().isWorker() && (target->isConstructing() || target->isRepairing()) && target->getDistance(ourBasePosition) < 1200)
	{
		return 12;
	}

	if (target->getType().isBuilding() && (target->isCompleted() || target->isBeingConstructed()) && target->getDistance(ourBasePosition) < 1200)
	{
		return 12;
	}

	bool isThreat = UnitUtil::TypeCanAttackGround(targetType);    // includes bunkers
	if (target->getType().isWorker())
	{
		isThreat = false;
	}

	// The most dangerous enemy units.
	if (targetType == BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode ||
		targetType == BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode ||
		targetType == BWAPI::UnitTypes::Protoss_High_Templar ||
		targetType == BWAPI::UnitTypes::Protoss_Reaver ||
		targetType == BWAPI::UnitTypes::Zerg_Infested_Terran ||
		targetType == BWAPI::UnitTypes::Zerg_Defiler)
	{
		return 12;
	}
	// something that can attack us or aid in combat
	if (isThreat)
	{
		return 11;
	}
	// next priority is any unit on the ground, or a nydus canal
	if (!targetType.isBuilding() || targetType == BWAPI::UnitTypes::Zerg_Nydus_Canal)
	{
		return 9;
	}

	// next is special buildings
	if (targetType.isResourceDepot())
	{
		return 7;
	}
	if (targetType == BWAPI::UnitTypes::Zerg_Spire)
	{
		return 6;
	}
	if (targetType == BWAPI::UnitTypes::Zerg_Spawning_Pool)
	{
		return 5;
	}
	if (targetType == BWAPI::UnitTypes::Protoss_Templar_Archives)
	{
		return 6;
	}
	if (targetType == BWAPI::UnitTypes::Protoss_Pylon)
	{
		return 5;
	}

	// any buildings that cost gas
	if (targetType.gasPrice() > 0)
	{
		return 4;
	}
	if (targetType.mineralPrice() > 0)
	{
		return 3;
	}

	// then everything else
	return 1;
}
