#include "BuildingPlacer.h"

#include "Bases.h"
#include "Common.h"
#include "InformationManager.h"
#include "MapTools.h"
#include "The.h"
#include "CombatCommander.h"

using namespace UAlbertaBot;

BuildingPlacer::BuildingPlacer()
	: the(The::Root())
{
    _reserveMap = std::vector< std::vector<bool> >(BWAPI::Broodwar->mapWidth(),std::vector<bool>(BWAPI::Broodwar->mapHeight(),false));

	reserveSpaceNearResources();
}

void BuildingPlacer::reserveSpaceNearResources()
{
	for (Base * base : Bases::Instance().getBases())
	{
		// A tile close to the center of the building (which is 4x3 tiles).
		BWAPI::TilePosition baseTile = base->getTilePosition() + BWAPI::TilePosition(2, 1);

		for (const auto mineral : base->getMinerals())
		{
			BWAPI::TilePosition minTile = mineral->getTilePosition();
			//reserveTiles(minTile, 2, 1);
			if (minTile.x < baseTile.x)
			{
				reserveTiles(minTile + BWAPI::TilePosition(2, 0), 1, 1);
			}
			else if (minTile.x > baseTile.x)
			{
				reserveTiles(minTile + BWAPI::TilePosition(-1, 0), 1, 1);
			}
			if (minTile.y < baseTile.y)
			{
				reserveTiles(minTile + BWAPI::TilePosition(0, 1), 2, 1);
			}
			else if (minTile.y > baseTile.y)
			{
				reserveTiles(minTile + BWAPI::TilePosition(0, -1), 2, 1);
			}
		}

		for (const auto gas : base->getGeysers())
		{
			// Don't build on the right edge or a right corner of a geyser.
			// It causes workers to take a long path around. Other locations are OK.
			BWAPI::TilePosition gasTile = gas->getTilePosition();
			//reserveTiles(gasTile, 4, 2);
			if (gasTile.x - baseTile.x > 2)
			{
				reserveTiles(gasTile + BWAPI::TilePosition(-1, 1), 3, 2);
			}
			else if (gasTile.x - baseTile.x < -2)
			{
				reserveTiles(gasTile + BWAPI::TilePosition(2, -1), 3, 2);
			}
			if (gasTile.y - baseTile.y > 2)
			{
				reserveTiles(gasTile + BWAPI::TilePosition(-1, -1), 2, 3);
			}
			else if (gasTile.y - baseTile.y < -2)
			{
				reserveTiles(gasTile + BWAPI::TilePosition(3, 0), 2, 3);
			}
		}
	}
}

BuildingPlacer & BuildingPlacer::Instance()
{
    static BuildingPlacer instance;
    return instance;
}

// makes final checks to see if a building can be built at a certain location
bool BuildingPlacer::canBuildHere(BWAPI::TilePosition position, const Building & b) const
{
    if (!BWAPI::Broodwar->canBuildHere(position,b.type,b.builderUnit))
    {
        return false;
    }

	// Check whether a worker can reach the place.
	// NOTE This simplified check disallows building on islands!
	if (!Bases::Instance().connectedToStart(position))
	{
		return false;
	}

    // check the reserve map
    for (int x = position.x; x < position.x + b.type.tileWidth(); x++)
    {
        for (int y = position.y; y < position.y + b.type.tileHeight(); y++)
        {
            if (_reserveMap[x][y])
            {
                return false;
            }
        }
    }

    // if it overlaps a base location return false
    if (tileOverlapsBaseLocation(position,b.type))
    {
        return false;
    }

    return true;
}

bool BuildingPlacer::tileBlocksAddon(BWAPI::TilePosition position) const
{
    for (int i=0; i<=2; ++i)
    {
        for (auto unit : BWAPI::Broodwar->getUnitsOnTile(position.x - i,position.y))
        {
            if (unit->getType() == BWAPI::UnitTypes::Terran_Command_Center ||
                unit->getType() == BWAPI::UnitTypes::Terran_Factory ||
                unit->getType() == BWAPI::UnitTypes::Terran_Starport ||
                unit->getType() == BWAPI::UnitTypes::Terran_Science_Facility)
            {
                return true;
            }
        }
    }

    return false;
}

// Can we build this building here with the specified amount of space around it?
// Space value is buildDist. horizontalOnly means only horizontal spacing.
bool BuildingPlacer::canBuildHereWithSpace(BWAPI::TilePosition position, const Building & b, int buildDist) const
{
    //if we can't build here, we of course can't build here with space
    if (!canBuildHere(position,b))
    {
        return false;
    }

    // height and width of the building
    int width(b.type.tileWidth());
    int height(b.type.tileHeight());

    //make sure we leave space for add-ons. These types of units can have addons:
    if (b.type == BWAPI::UnitTypes::Terran_Command_Center ||
        b.type == BWAPI::UnitTypes::Terran_Factory ||
        b.type == BWAPI::UnitTypes::Terran_Starport ||
        b.type == BWAPI::UnitTypes::Terran_Science_Facility)
    {
        width += 2;
    }

    // define the rectangle of the building spot
    int startx = position.x - buildDist;
    int starty = position.y - buildDist;
    int endx   = position.x + width + buildDist;
    int endy   = position.y + height + buildDist;

    if (b.type.isAddon())
    {
        const BWAPI::UnitType builderType = b.type.whatBuilds().first;

        BWAPI::TilePosition builderTile(position.x - builderType.tileWidth(),position.y + 2 - builderType.tileHeight());

        startx = builderTile.x - buildDist;
        starty = builderTile.y - buildDist;
        endx = position.x + width + buildDist;
        endy = position.y + height + buildDist;
    }
\
    // if this rectangle doesn't fit on the map we can't build here
    if (startx < 0 || starty < 0 || endx > BWAPI::Broodwar->mapWidth() || endy > BWAPI::Broodwar->mapHeight())
    {
        return false;
    }

    // if space is reserved, or it's in the resource box, we can't build here
    for (int x = startx; x < endx; x++)
    {
        for (int y = starty; y < endy; y++)
        {
            if (!b.type.isRefinery())
            {
                if (!buildable(b,x,y) || _reserveMap[x][y])
                {
                    return false;
                }
            }
        }
    }

    return true;
}

BWAPI::TilePosition BuildingPlacer::getBuildLocationNear(const Building & b, int buildDist) const
{
	/*
	if (b.type == BWAPI::UnitTypes::Terran_Barracks)
	{
		Base * base = Bases::Instance().myStartingBase();
		BWAPI::Position pos = BWTA::getNearestChokepoint(base->getTilePosition())->getCenter();

		// BWAPI::Broodwar->printf("Building Placer seeks position near %d, %d", b.desiredPosition.x, b.desiredPosition.y);

		// get the precomputed vector of tile positions which are sorted closest to this location
		const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(BWAPI::TilePosition(pos));

		//const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(b.desiredPosition);

		// iterate through the list until we've found a suitable location
		for (size_t i(0); i < closestToBuilding.size(); ++i)
		{
			if (canBuildHereWithSpace(closestToBuilding[i], b, 1))
			{
				return closestToBuilding[i];
			}
		}

		return  BWAPI::TilePositions::None;
	}
	*/

		const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(b.desiredPosition);
		// iterate through the list until we've found a suitable location
		for (size_t i(0); i < closestToBuilding.size(); ++i)
		{
			if (canBuildHereWithSpace(closestToBuilding[i], b, buildDist))
			{
				return closestToBuilding[i];
			}
		}

		return  BWAPI::TilePositions::None;
	
}

BWAPI::TilePosition BuildingPlacer::getBuildLocationNear(const Building & b, int buildDist, BWAPI::Position center) const
{
	const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(center);
	// iterate through the list until we've found a suitable location
	for (size_t i(0); i < closestToBuilding.size(); ++i)
	{
		if (canBuildHereWithSpace(closestToBuilding[i], b, buildDist))
		{
			return closestToBuilding[i];
		}
	}
	return  BWAPI::TilePositions::None;
}

bool BuildingPlacer::tileOverlapsBaseLocation(BWAPI::TilePosition tile, BWAPI::UnitType type) const
{
    // if it's a resource depot we don't care if it overlaps
    if (type.isResourceDepot())
    {
        return false;
    }

    // dimensions of the proposed location
    int tx1 = tile.x;
    int ty1 = tile.y;
    int tx2 = tx1 + type.tileWidth();
    int ty2 = ty1 + type.tileHeight();

    // for each base location
    for (BWTA::BaseLocation * base : BWTA::getBaseLocations())
    {
        // dimensions of the base location
        int bx1 = base->getTilePosition().x;
        int by1 = base->getTilePosition().y;
        int bx2 = bx1 + BWAPI::Broodwar->self()->getRace().getCenter().tileWidth();
        int by2 = by1 + BWAPI::Broodwar->self()->getRace().getCenter().tileHeight();

        // conditions for non-overlap are easy
        bool noOverlap = (tx2 < bx1) || (tx1 > bx2) || (ty2 < by1) || (ty1 > by2);

        // if the reverse is true, return true
        if (!noOverlap)
        {
            return true;
        }
    }

    // otherwise there is no overlap
    return false;
}

bool BuildingPlacer::buildable(const Building & b,int x,int y) const
{
	BWAPI::TilePosition tp(x, y);

	if (!tp.isValid())
	{
		return false;
	}

	if (!BWAPI::Broodwar->isBuildable(x, y, true))
    {
		// Unbuildable according to the map, or because the location is blocked
		// by a visible building. Unseen buildings (even if known) are "buildable" on.
        return false;
    }

	if (BWAPI::Broodwar->self()->getRace() == BWAPI::Races::Terran && tileBlocksAddon(tp))
    {
        return false;
    }

	// getUnitsOnTile() only returns visible units, even if they are buildings.
    for (const auto unit : BWAPI::Broodwar->getUnitsOnTile(x,y))
    {
        if (b.builderUnit != nullptr && unit != b.builderUnit)
        {
            return false;
        }
    }

    return true;
}

void BuildingPlacer::reserveTiles(BWAPI::TilePosition position, int width, int height)
{
    int rwidth = _reserveMap.size();
    int rheight = _reserveMap[0].size();
    for (int x = std::max(position.x, 0); x < std::min(position.x + width, rwidth); x++)
    {
        for (int y = std::max(position.y, 0); y < std::min(position.y + height, rheight); y++)
        {
            _reserveMap[x][y] = true;
        }
    }
}

void BuildingPlacer::drawReservedTiles()
{
    if (!Config::Debug::DrawReservedBuildingTiles)
    {
        return;
    }

    int rwidth = _reserveMap.size();
    int rheight = _reserveMap[0].size();

    for (int x = 0; x < rwidth; ++x)
    {
        for (int y = 0; y < rheight; ++y)
        {
            if (_reserveMap[x][y])
            {
                int x1 = x*32 + 3;
                int y1 = y*32 + 3;
                int x2 = (x+1)*32 - 3;
                int y2 = (y+1)*32 - 3;

                BWAPI::Broodwar->drawBoxMap(x1, y1, x2, y2, BWAPI::Colors::Yellow);
            }
        }
    }
}

void BuildingPlacer::freeTiles(BWAPI::TilePosition position, int width, int height)
{
    int rwidth = _reserveMap.size();
    int rheight = _reserveMap[0].size();

    for (int x = position.x; x < position.x + width && x < rwidth; x++)
    {
        for (int y = position.y; y < position.y + height && y < rheight; y++)
        {
            _reserveMap[x][y] = false;
        }
    }
}

// NOTE This allows building only on accessible geysers.
BWAPI::TilePosition BuildingPlacer::getRefineryPosition()
{
    BWAPI::TilePosition closestGeyser = BWAPI::TilePositions::None;
    int minGeyserDistanceFromHome = 100000;
	BWAPI::Position homePosition = InformationManager::Instance().getMyMainBaseLocation()->getPosition();

	// NOTE In BWAPI 4.2.1 getStaticGeysers() has a bug affecting geysers whose refineries
	// have been canceled or destroyed: They become inaccessible. https://github.com/bwapi/bwapi/issues/697
	for (const auto geyser : BWAPI::Broodwar->getGeysers())
	{
        // check to see if it's near one of our depots
        bool nearDepot = false;
        for (const auto unit : BWAPI::Broodwar->self()->getUnits())
        {
            if (unit->getType().isResourceDepot() && unit->getDistance(geyser) < 300)
            {
                nearDepot = true;
				break;
            }
        }

        if (nearDepot)
        {
            int homeDistance = geyser->getDistance(homePosition);

            if (homeDistance < minGeyserDistanceFromHome)
            {
                minGeyserDistanceFromHome = homeDistance;
                closestGeyser = geyser->getTilePosition();      // BWAPI bug workaround by Arrak
            }
		}
    }
    
    return closestGeyser;
}

//////////////////////////////////////////////////////////////++
BWAPI::TilePosition BuildingPlacer::getBunkerPosition(const Building & b, int numBunker)
{

	BWAPI::Position pos = bunkerBuildPlace;
	if (numBunker == 0)
	{
		//读取Json中的位置坐标
		BWAPI::TilePosition bunkerLocation = BWAPI::TilePosition(bunkerBuildPlace);
		if (canBuildHereWithSpace(bunkerLocation, b, 0))
		{
			return bunkerLocation;
		}
		else
		{
			const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(bunkerLocation);
			for (size_t i(0); i < closestToBuilding.size(); ++i)
			{
				if (canBuildHereWithSpace(closestToBuilding[i], b, 0))
				{
					return closestToBuilding[i];
				}
			}
			return  BWAPI::TilePositions::None;
		}

		/*
		//在bunkerLocation附近寻找可以建造的位置
		if (canBuildHereWithSpace(bunkerLocation, b, 0))
		{
			return bunkerLocation;
		}
		return  BWAPI::TilePositions::None;
		*/
	}
	else
	{
		BWAPI::TilePosition bunkerLocation = BWAPI::TilePosition(bunkerBuildPlace);

		const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(bunkerLocation);
		for (size_t i(0); i < closestToBuilding.size(); ++i)
		{
			if (canBuildHereWithSpace(closestToBuilding[i], b, 0))
			{
				return closestToBuilding[i];
			}
		}
		return BWAPI::TilePositions::None;
	}
	/*
	if (numBunker == 0)
	{
		//读取Json中的位置坐标
		BWAPI::TilePosition bunkerLocation = BWAPI::TilePosition(InformationManager::Instance().getPosition("bunkerPoint"));

		//在bunkerLocation附近寻找可以建造的位置
		if (canBuildHereWithSpace(bunkerLocation, b, 0))
			{
			return bunkerLocation;
			}
		return BuildingPlacer::Instance().getBuildLocationNear(b, 1);

		//return  BWAPI::TilePositions::None;
	}
	
	if (BWAPI::Broodwar->mapFileName() == "(2)Destination.scx")
	{
		if (numBunker == 1)
		{
			BWAPI::TilePosition bunkerLocation = BWAPI::TilePosition(InformationManager::Instance().getPosition("bunkerPoint3"));

			const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(bunkerLocation);
			for (size_t i(0); i < closestToBuilding.size(); ++i)
			{
				if (canBuildHereWithSpace(closestToBuilding[i], b, 0))
				{
					return closestToBuilding[i];
				}
			}
			return BuildingPlacer::Instance().getBuildLocationNear(b, 1);
		}
		else if (numBunker == 2)
		{
			BWAPI::TilePosition bunkerLocation = BWAPI::TilePosition(InformationManager::Instance().getPosition("chokePoint2"));

			const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(bunkerLocation);
			for (size_t i(0); i < closestToBuilding.size(); ++i)
			{
				if (canBuildHereWithSpace(closestToBuilding[i], b, 0))
				{
					return closestToBuilding[i];
				}
			}
			return BuildingPlacer::Instance().getBuildLocationNear(b, 1);
		}
		else if (numBunker == 3)
		{
			BWAPI::TilePosition bunkerLocation = BWAPI::TilePosition(InformationManager::Instance().getPosition("bunkerPoint4"));

			const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(bunkerLocation);
			for (size_t i(0); i < closestToBuilding.size(); ++i)
			{
				if (canBuildHereWithSpace(closestToBuilding[i], b, 0))
				{
					return closestToBuilding[i];
				}
			}
			return BuildingPlacer::Instance().getBuildLocationNear(b, 1);
		}
		else
			return BuildingPlacer::Instance().getBuildLocationNear(b, 1);
	}
	else 
	{
		BWAPI::TilePosition bunkerLocation = BWAPI::TilePosition(InformationManager::Instance().getPosition("chokePoint2"));

		const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(bunkerLocation);
		for (size_t i(0); i < closestToBuilding.size(); ++i)
		{
			if (canBuildHereWithSpace(closestToBuilding[i], b, 0))
			{
				return closestToBuilding[i];
			}
		}
		return BuildingPlacer::Instance().getBuildLocationNear(b, 1);
	}
	*/

}
//8.7.2019
BWAPI::Position BuildingPlacer::getBaseTurretPosition(const Building & b)
{
	BWAPI::Position outputPos;

	BWAPI::Position myStarBasePosition = Bases::Instance().myStartingBase()->getPosition();
	BWAPI::Position minMineralPosition;

	/*
	for (const auto& mineral : BWTA::getStartLocation(BWAPI::Broodwar->self())->getStaticMinerals())
	{
		if (mineral->getInitialResources() <= 64)  continue;// 排除零碎小矿
		minePos = mineral->getInitialPosition();
		outputPos = BWAPI::Position((minePos.x + myStarBasePosition.x) / 2, (minePos.y + myStarBasePosition.y) / 2);
		outputPos = BWAPI::Position((minePos.x + outputPos.x) / 2, (minePos.y + outputPos.y) / 2);
		//查看这里是否已经修了防空塔
		BWAPI::Unit turret = BWAPI::Broodwar->getClosestUnit(
			outputPos,
			BWAPI::Filter::IsOwned && BWAPI::Filter::GetType == BWAPI::UnitTypes::Terran_Missile_Turret,
			80);
		if (!turret)
		{
			return outputPos;
		}
		else
		{
			continue;
		}
	}
	*/
	//获取中间矿的位置
	int numMinerals = 0;
	for (const auto& mineral : BWTA::getStartLocation(BWAPI::Broodwar->self())->getStaticMinerals())
	{
		numMinerals += 1;
		int size = BWTA::getStartLocation(BWAPI::Broodwar->self())->getStaticMinerals().size();
		if (numMinerals == size / 2)
		{
			minMineralPosition = mineral->getInitialPosition();
			break;
		}
	}

	//计算基地中心已经拥有的防空台的数量
	int numTurret = 0;
	for (BWAPI::Unit unit : BWAPI::Broodwar->self()->getUnits())
	{
		if (unit->getType() == BWAPI::UnitTypes::Terran_Missile_Turret &&
			unit->getDistance(Bases::Instance().myStartingBase()->getPosition()) < 260)
		{
			numTurret += 1;
		}
	}


	//假如矿在基地的左边
	if (minMineralPosition.x < myStarBasePosition.x)
	{
		//第一个
		if (numTurret == 0)
		{
			outputPos.x = myStarBasePosition.x - 30;
			outputPos.y = myStarBasePosition.y - 60;
		}
		//第二个
		else if (numTurret == 1)
		{
			outputPos.x = myStarBasePosition.x - 30;
			outputPos.y = myStarBasePosition.y + 120;
		}
		//第三个
		else
		{
			outputPos = myStarBasePosition;
		}
		const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(outputPos);
		for (size_t i(0); i < closestToBuilding.size(); ++i)
		{
			if (canBuildHereWithSpace(closestToBuilding[i], b, 0))
			{
				return BWAPI::Position(closestToBuilding[i]);
			}
		}

		return BWAPI::Positions::None;
	}
	//假如矿在基地的右边
	else 
	{
		if (numTurret == 0)
		{
			outputPos.x = myStarBasePosition.x + 130;
			outputPos.y = myStarBasePosition.y - 60;
		}
		else if (numTurret == 1)
		{
			outputPos.x = myStarBasePosition.x + 130;
			outputPos.y = myStarBasePosition.y + 120;
		}
		//第三个
		else
		{
			outputPos = myStarBasePosition;
		}
		const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(outputPos);
		for (size_t i(0); i < closestToBuilding.size(); ++i)
		{
			if (canBuildHereWithSpace(closestToBuilding[i], b, 0))
			{
				return BWAPI::Position(closestToBuilding[i]);
			}
		}

		return BWAPI::Positions::None;
	}
}


BWAPI::TilePosition BuildingPlacer::getTurretPosition(const Building & b, int num)
{

	//主基地和矿之间的一个防空台，位置固定
	if (Bases::Instance().myStartingBase()->getDistance(TurretBuildPlace) < 200)
	{
		return BWAPI::TilePosition(getBaseTurretPosition(b));
	}

	//其它位置的防空台，返回一个最近的可建造点

	BWAPI::TilePosition turretLocation = BWAPI::TilePosition(TurretBuildPlace);

	const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(turretLocation);
	for (size_t i(0); i < closestToBuilding.size(); ++i)
	{
		if (canBuildHereWithSpace(closestToBuilding[i], b, 0))			{
			return closestToBuilding[i];
		}
	}
	return BWAPI::TilePositions::None;

	/*
	if (BWAPI::Broodwar->mapFileName() == "(2)Destination.scx")
	{
		if (num == 0)
		{
			Base * base = Bases::Instance().frontBase();
			BWAPI::Position pos = base->getPosition();

			const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(pos);
			for (size_t i(0); i < closestToBuilding.size(); ++i)
			{
				if (canBuildHereWithSpace(closestToBuilding[i], b, 0))
				{
					return closestToBuilding[i];
				}
			}
			return BuildingPlacer::Instance().getBuildLocationNear(b, 1);
		}

		if (num == 1)
		{
			BWAPI::TilePosition bunkerLocation = BWAPI::TilePosition(InformationManager::Instance().getPosition("bunkerPoint3"));

			const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(bunkerLocation);
			for (size_t i(0); i < closestToBuilding.size(); ++i)
			{
				if (canBuildHereWithSpace(closestToBuilding[i], b, 0))
				{
					return closestToBuilding[i];
				}
			}
			return BuildingPlacer::Instance().getBuildLocationNear(b, 1);
		}
		else if (num == 2)
		{
			BWAPI::TilePosition bunkerLocation = BWAPI::TilePosition(InformationManager::Instance().getPosition("chokePoint2"));

			const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(bunkerLocation);
			for (size_t i(0); i < closestToBuilding.size(); ++i)
			{
				if (canBuildHereWithSpace(closestToBuilding[i], b, 0))
				{
					return closestToBuilding[i];
				}
			}
			return BuildingPlacer::Instance().getBuildLocationNear(b, 1);
		}
		else if (num == 3)
		{
			BWAPI::TilePosition bunkerLocation = BWAPI::TilePosition(InformationManager::Instance().getPosition("bunkerPoint4"));

			const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(bunkerLocation);
			for (size_t i(0); i < closestToBuilding.size(); ++i)
			{
				if (canBuildHereWithSpace(closestToBuilding[i], b, 0))
				{
					return closestToBuilding[i];
				}
			}
			return BuildingPlacer::Instance().getBuildLocationNear(b, 1);
		}

		else
			return BuildingPlacer::Instance().getBuildLocationNear(b, 1);
	}
	else
		return BuildingPlacer::Instance().getBuildLocationNear(b, 1);
		*/
}

////6.24
BWAPI::TilePosition  BuildingPlacer::getFactoryPosition(const Building & b, int num)
{
	//最多建造5个工厂
	if (num >= 6)
	{
		return BWAPI::TilePositions::None;
	}
	//全部建造在主基地区域里面
	BWAPI::Position pos = Bases::Instance().myStartingBase()->getPosition();
	const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(pos);
	for (size_t i(0); i < closestToBuilding.size(); ++i)
	{
		if (canBuildHereWithSpace(closestToBuilding[i], b, 1))
		{
			return closestToBuilding[i];
		}
	}
	return BWAPI::TilePositions::None;
}

//7.31.2019
BWAPI::TilePosition BuildingPlacer::getNearBasePosition(const Building & b)
{
	//全部建造在主基地区域里面
	BWAPI::Position pos = Bases::Instance().myStartingBase()->getPosition();
	const std::vector<BWAPI::TilePosition> & closestToBuilding = MapTools::Instance().getClosestTilesTo(pos);
	for (size_t i(0); i < closestToBuilding.size(); ++i)
	{
		if (canBuildHereWithSpace(closestToBuilding[i], b, 1))
		{
			return closestToBuilding[i];
		}
	}
	return BWAPI::TilePositions::None;
}


BWAPI::TilePosition BuildingPlacer::getSupplyDepotPosition(const Building & b, int numSD)
{
	BWAPI::Position supplypoint0 = InformationManager::Instance().getPosition("supplyPoint");

	if (numSD == 0)
	{
		if (canBuildHereWithSpace(BWAPI::TilePosition(supplypoint0), b, 0))
		{
			return BWAPI::TilePosition(supplypoint0);
			allowBuildSupply = false;
		}

		return BWAPI::TilePositions::None;
	}
	     allowBuildSupply = false;
		 return BuildingPlacer::Instance().getBuildLocationNear(b, 1);

}

BWAPI::TilePosition BuildingPlacer::getBarracksPosition(const Building & b, int numBarracks)
{
	if (numBarracks == 0)
	{
		BWAPI::Position barrackspoint = InformationManager::Instance().getPosition("barracksPoint");

		if (canBuildHereWithSpace(BWAPI::TilePosition(barrackspoint), b, 0))
		{
			return BWAPI::TilePosition(barrackspoint);
		}
		else
			//return  BuildingPlacer::Instance().getBuildLocationNear(b, 1);
			return BWAPI::TilePositions::None;
	}
	else if (numBarracks == 1)
	{
		BWAPI::Unit barracks = BWAPI::Broodwar->getClosestUnit(
			InformationManager::Instance().getPosition("chokePoint2"),
			BWAPI::Filter::IsOwned && BWAPI::Filter::GetType == BWAPI::UnitTypes::Terran_Barracks,
			100);
		if (!barracks)
		{
			for (const BWAPI::TilePosition & tile : MapTools::Instance().getClosestTilesTo(InformationManager::Instance().getPosition("chokePoint2")))
			{
				if (canBuildHereWithSpace(tile, b, 1))
				{
					return tile;
				}
			}
			return BWAPI::TilePositions::None;
		}
		else
		{
			for (const BWAPI::TilePosition & tile : MapTools::Instance().getClosestTilesTo(CombatCommander::Instance().getDefenseLine()))
			{
				if (canBuildHereWithSpace(tile, b, 0))
				{
					return tile;
				}
			}
			return BWAPI::TilePositions::None;
		}
	}
	else
	{
		for (const BWAPI::TilePosition & tile : MapTools::Instance().getClosestTilesTo(Bases::Instance().myStartingBase()->getPosition()))
		{
			if (canBuildHereWithSpace(tile, b, 1))
			{
				return tile;
			}
		}
		return BWAPI::TilePositions::None;
	}
		return BWAPI::TilePositions::None;
}

BWAPI::TilePosition BuildingPlacer::getEdgeBuildingPosition(const Building & b)
{
	Base * base = Bases::Instance().myStartingBase();
	BWAPI::TilePosition basePosition = base->getTilePosition();
	BWAPI::Position chokePosition = BWTA::getNearestChokepoint(base->getTilePosition())->getCenter();

	if (b.type == BWAPI::UnitTypes::Terran_Supply_Depot)
	{
		allowBuildSupply = false;
	}

	for (int numRows = 0; numRows < 2; numRows++)
	{
		for (const BWAPI::TilePosition & tile : MapTools::Instance().getClosestTilesTo(basePosition))
		{
			int dist = base->getDistance(BWAPI::Position(tile));
			int dist2 = chokePosition.getDistance(BWAPI::Position(tile));

			//BWAPI::Region BaseRegion = BWAPI::Region(base);
			if (BWTA::getRegion(tile) != BWTA::getRegion(base->getPosition()) || dist > 1000 || dist2 < 280){ continue; }

			// Left.
			if (tile.x == b.type.tileWidth() * numRows)
			{
				if (canBuildHereWithSpace(tile, b, 0) && freeOnRight(tile, b.type))
				{
					return tile;
				}
			}

			// Top.
			else if (tile.y == b.type.tileHeight() * numRows)
			{
				if (canBuildHereWithSpace(tile, b, 0) && freeOnBottom(tile, b.type))
				{
					return tile;
				}
			}

			// Right.
			else if (tile.x + b.type.tileWidth() + b.type.tileWidth() * numRows == BWAPI::Broodwar->mapWidth())
			{
				if (canBuildHereWithSpace(tile, b, 0) && freeOnLeft(tile, b.type))
				{
					return tile;
				}
			}
			// Bottom.
			// NOTE The map's bottom row of tiles is not buildable (though other edges are).
			else if (tile.y + b.type.tileHeight() + b.type.tileHeight() * numRows == BWAPI::Broodwar->mapHeight() - 1)
			{
				if (canBuildHereWithSpace(tile, b, 0) && freeOnTop(tile, b.type))
				{
					return tile;
				}
			}
		}
	}
	
	for (const BWAPI::TilePosition & tile : MapTools::Instance().getClosestTilesTo(basePosition))
	{
		int dist = base->getDistance(BWAPI::Position(tile));
		BWAPI::Region BaseRegion = BWAPI::Region(base);
		if (BWTA::getRegion(tile) != BWTA::getRegion(base->getPosition()) || dist > 1000){ continue; }
		if (dist > 700)
		{
			int dist0 = chokePosition.getDistance(BWAPI::Position(tile));
			if (canBuildHereWithSpace(tile, b, 0) && dist0 > 300)
			{
				return tile;
			}
		}
	}
	
	// It's not on an edge.
	return  BuildingPlacer::Instance().getBuildLocationNear(b, 1);
}

bool BuildingPlacer::freeOnTop(const BWAPI::TilePosition & tile, BWAPI::UnitType buildingType) const
{
	int x1 = tile.x - 1;
	int x2 = tile.x + buildingType.tileWidth();
	int y = tile.y - 1;
	if (y < 0 || x1 < 0 || x2 >= BWAPI::Broodwar->mapWidth())
	{
		return false;
	}

	for (int x = x1; x <= x2; ++x)
	{
		if (!freeTile(x, y))
		{
			return false;
		}
	}
	return true;
}

bool BuildingPlacer::freeOnRight(const BWAPI::TilePosition & tile, BWAPI::UnitType buildingType) const
{
	int x = tile.x + buildingType.tileWidth();
	int y1 = tile.y - 1;
	int y2 = tile.y + buildingType.tileHeight();
	if (x >= BWAPI::Broodwar->mapWidth() || y1 < 0 || y2 >= BWAPI::Broodwar->mapHeight())
	{
		return false;
	}

	for (int y = y1; y <= y2; ++y)
	{
		if (!freeTile(x, y))
		{
			return false;
		}
	}
	return true;
}

bool BuildingPlacer::freeOnLeft(const BWAPI::TilePosition & tile, BWAPI::UnitType buildingType) const
{
	int x = tile.x - 1;
	int y1 = tile.y - 1;
	int y2 = tile.y + buildingType.tileHeight();
	if (x < 0 || y1 < 0 || y2 >= BWAPI::Broodwar->mapHeight())
	{
		return false;
	}

	for (int y = y1; y <= y2; ++y)
	{
		if (!freeTile(x, y))
		{
			return false;
		}
	}
	return true;
}

bool BuildingPlacer::freeOnBottom(const BWAPI::TilePosition & tile, BWAPI::UnitType buildingType) const
{
	int x1 = tile.x - 1;
	int x2 = tile.x + buildingType.tileWidth();
	int y = tile.y + buildingType.tileHeight();
	if (y >= BWAPI::Broodwar->mapHeight() || x1 < 0 || x2 >= BWAPI::Broodwar->mapWidth())
	{
		return false;
	}

	for (int x = x1; x <= x2; ++x)
	{
		if (!freeTile(x, y))
		{
			return false;
		}
	}
	return true;
}

// The tile is free of permanent obstacles, including future ones from planned buildings.
// There might be a unit passing through, though.
// The caller must ensure that x and y are in range!
bool BuildingPlacer::freeTile(int x, int y) const
{
	UAB_ASSERT(BWAPI::TilePosition(x, y).isValid(), "bad tile");

	if (!BWAPI::Broodwar->isBuildable(x, y, true) || _reserveMap[x][y])
	{
		return false;
	}
	if (BWAPI::Broodwar->self()->getRace() == BWAPI::Races::Terran && tileBlocksAddon(BWAPI::TilePosition(x, y)))
	{
		return false;
	}

	return true;
}
//////////////////////////////////////////////////////////////__

bool BuildingPlacer::isReserved(int x, int y) const
{
    int rwidth = _reserveMap.size();
    int rheight = _reserveMap[0].size();
    if (x < 0 || y < 0 || x >= rwidth || y >= rheight)
    {
        return false;
    }

    return _reserveMap[x][y];
}
