#include "FleeManager.h"

FleeManager::FleeManager(){}

std::set<Block*> FleeManager::getBlocks(){
	return blocks;
}

Block* FleeManager::getBestBlock(){
	Block* b;
	double bestValue;
	if (std::numeric_limits<double>::is_iec559) bestValue = std::numeric_limits<double>::infinity();
	for (auto& block : blocks){
		if ((*block).getValue() < bestValue){
			bestValue = (*block).getValue();
			b = block;
		}
		else if ((*block).getValue() == bestValue){
			if (Utils::flipCoin(0.5)){
				b = block;
			}
		}
	}
	return b;
}

void FleeManager::erase(Block *b){
	blocks.erase(b);
}

void FleeManager::updateBlocks(BWAPI::Unit u){
	blocks.clear();
	int sightRange = u->getType().sightRange(); // get the sight range of the unit.

	if (sightRange % BLOCKSIZE){
		for (int i = -sightRange; i < sightRange; i += BLOCKSIZE){
			for (int j = -sightRange; j < sightRange; j += BLOCKSIZE){
				// Check corners
				if (checkCorners(i, j, u))
					insertBlock(i, j, u);
			}
		}
	}
	else{
		for (int i = -sightRange + BLOCKSIZE / 2; i < sightRange - BLOCKSIZE / 2; i += BLOCKSIZE){
			for (int j = -sightRange + BLOCKSIZE / 2; j < sightRange - BLOCKSIZE / 2; j += BLOCKSIZE){
				// Check corners
				if (checkCorners(i, j, u))
					insertBlock(i, j, u);
			}
		}
	}

	// Gets all the enemies in the unit's sight range
	BWAPI::Unitset foes = u->getUnitsInRadius(sightRange, BWAPI::Filter::IsEnemy);
	for (auto& foe : foes){
		updateEnemy(foe);
	}
}

// Checks if the block is inside the agent's sight range.
bool FleeManager::checkCorners(int i, int j, BWAPI::Unit u){
	int sightRange = u->getType().sightRange(); // get the sight range of the unit.
	int corners = 4;

	BWAPI::Position topLeft(u->getPosition().x + i, u->getPosition().y + j);
	if (topLeft.getDistance(u->getPosition()) >= sightRange){
		corners--;
	}
	BWAPI::Position topRight(u->getPosition().x + i + BLOCKSIZE, u->getPosition().y + j);
	if (topRight.getDistance(u->getPosition()) >= sightRange){
		corners--;
	}
	BWAPI::Position bottomLeft(u->getPosition().x + i, u->getPosition().y + j + BLOCKSIZE);
	if (bottomLeft.getDistance(u->getPosition()) >= sightRange){
		corners--;
	}
	BWAPI::Position bottomRight(u->getPosition().x + i + BLOCKSIZE, u->getPosition().y + j + BLOCKSIZE);
	if (bottomRight.getDistance(u->getPosition()) >= sightRange){
		corners--;
	}
	BWAPI::Position center(u->getPosition().x + i + BLOCKSIZE / 2, u->getPosition().y + j + BLOCKSIZE / 2);

	if (corners > 2){
		return true;
	}
	else if (corners == 2){
		if (center.getDistance(u->getPosition()) < sightRange){
			return true;
		}
	}

	return false;
}

void FleeManager::insertBlock(int i, int j, BWAPI::Unit u){
	BWAPI::Position topLeft(u->getPosition().x + i, u->getPosition().y + j);
	Block *block = new Block(topLeft);
	//blocks.emplace(block);
	auto ret = blocks.emplace(block); // DEBUG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	if (!ret.second) BWAPI::Broodwar->sendText("Error: Block already emplaced."); // DEBUG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}

void FleeManager::updateEnemy(BWAPI::Unit foe){
	// Find enemy's block
	Block *enemyBlock;
	for (auto& block : blocks){
		if ((*block).hasInside(foe->getPosition())){
			enemyBlock = block;
			break;
		}
	}
	// Update all blocks to enemy distance
	for (auto& block : blocks){
		int md = Block::manhattanDistance((*block).getTopLeft(), (*enemyBlock).getTopLeft());
		double value = 100 / (md + 1);
		(*block).addValue(value);
	}
}
