#include "ConfidenceMap.h"

using namespace MyBot;


void ConfidenceMap::Add(BWAPI::Unit unit)
{
	if (unit == nullptr || unit->exists() == false 
		|| unit->getType().isNeutral() || unit->getType() == BWAPI::UnitTypes::Unknown || unit->getType() == BWAPI::UnitTypes::None) {
		return;
	}
	
	if (isToFindError) std::cout << "c";

	BWAPI::TilePosition unitTilePosition = BWAPI::TilePosition(unit->getPosition());
	BWAPI::Position unitPosition = unit->getPosition();
	BWAPI::TilePosition aroundTilePosition;
	BWAPI::Position aroundPosition;

	Cell & cell = GetCell(unitTilePosition);
	if (unit->getPlayer() == BWAPI::Broodwar->self()) {
		auto & List = cell.myUnits;
		if (!contains(List, unit)) List.push_back(unit);
	}
	else {
		auto & List = cell.enemyUnits;
		if (!contains(List, unit)) List.push_back(unit);

		if (isToFindError) std::cout << "d";
		// 적군의 DPS 만 업데이트
		// 일꾼도 공격 가능하지만, 일꾼은 숫자가 많아서 제외
		// 캐리어 인터셉터은 빼고 캐리어만 고려
		// TODO : 리버 
		if (unit->getType().isWorker() == false && unit->isCompleted() == true
			&& unit->getType() != BWAPI::UnitTypes::Protoss_Interceptor) {

			int maxRange = 0;				
			BWAPI::WeaponType airWeaponType = unit->getType().airWeapon();
				
			if (isToFindError) std::cout << "e";
			//std::cout << unit->getType().getName().c_str() << " ";
			//std::cout << airWeaponType.getName().c_str() << " "
			//	<< airWeaponType.damageAmount() << " " << airWeaponType.damageFactor() << " "
			//	<< airWeaponType.damageBonus() << " " << airWeaponType.damageCooldown() << " "
			//	<< airWeaponType.damageType() << " " << airWeaponType.explosionType() << " "
			//	<< airWeaponType.maxRange() << " " << std::endl;


			// 공중 공격 가능 여부
			bool isAirAttackable = false;
			if (airWeaponType != BWAPI::WeaponTypes::None && airWeaponType != BWAPI::WeaponTypes::Unknown) {
				isAirAttackable = true;
			}
			// 캐리어는 인터셉터 생각해야함
			else if (unit->getType() == BWAPI::UnitTypes::Protoss_Carrier) {
				isAirAttackable = true;
				airWeaponType = BWAPI::WeaponTypes::Pulse_Cannon;
			}
			// 벙커는 마린 들어있는 것으로 생각해야함
			else if (unit->getType() == BWAPI::UnitTypes::Terran_Bunker) {
				isAirAttackable = true;
				airWeaponType = BWAPI::WeaponTypes::Gauss_Rifle;
			}
			// TODO : 마법유닛 고려

			if (isAirAttackable ) {
				maxRange = airWeaponType.maxRange();
				for (int i = (-1) * maxRange; i <= maxRange; i += 32) {
					for (int j = (-1) * maxRange; j <= maxRange; j += 32) {
						aroundPosition = BWAPI::Position(unitPosition.x + i, unitPosition.y + j);
						if (aroundPosition.isValid() == false) continue;
						if (unitPosition.getDistance(aroundPosition) > maxRange) continue;

						aroundTilePosition = BWAPI::TilePosition(aroundPosition);
						if (aroundTilePosition.isValid()) {
							Cell & aroundCell = GetCell(aroundTilePosition);

							// 1000 프레임동안 얼마나 Damage 를 줄 수 있는가
							aroundCell.airDamagePerSecond += (int)(airWeaponType.damageAmount() * (1000 / airWeaponType.damageCooldown()));
						}
					}
				}
			}
				
			if (isToFindError) std::cout << "f";

			BWAPI::WeaponType groundWeaponType = unit->getType().groundWeapon();

			// 지상 공격 가능 여부
			bool isGroundAttackable = false;
			if (groundWeaponType != BWAPI::WeaponTypes::None && groundWeaponType != BWAPI::WeaponTypes::Unknown) {
				isGroundAttackable = true;
			}
			// 캐리어는 인터셉터 생각해야함
			else if (unit->getType() == BWAPI::UnitTypes::Protoss_Carrier) {
				isGroundAttackable = true;
				groundWeaponType = BWAPI::WeaponTypes::Pulse_Cannon;
			}
			// 벙커는 마린 들어있는 것으로 생각해야함
			else if (unit->getType() == BWAPI::UnitTypes::Terran_Bunker) {
				isGroundAttackable = true;
				groundWeaponType = BWAPI::WeaponTypes::Gauss_Rifle;
			}
			// TODO : 마법유닛 고려

			//std::cout << groundWeaponType.getName().c_str() << " "
			//	<< groundWeaponType.damageAmount() << " " << groundWeaponType.damageFactor() << " "
			//	<< groundWeaponType.damageBonus() << " " << groundWeaponType.damageCooldown() << " "
			//	<< groundWeaponType.damageType() << " " << groundWeaponType.explosionType() << " "
			//	<< groundWeaponType.maxRange() << " " << std::endl;

			if (isGroundAttackable) {
				maxRange = groundWeaponType.maxRange();
				for (int i = (-1) *maxRange; i <= maxRange; i += TILE_SIZE) {
					for (int j = (-1) * maxRange; j <= maxRange; j += TILE_SIZE) {

						// 시즈탱크만 Min Range 가 존재
						if (unit->getType() == BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode) {
							if (i == -32 || i == 0 || i == 32 
								|| j == -32 || j == 0 || j == 32) {
								continue;
							}
						}

						aroundPosition = BWAPI::Position(unitPosition.x + i, unitPosition.y + j);
						if (aroundPosition.isValid() == false) continue;
						if (unitPosition.getDistance(aroundPosition) > maxRange) continue;

						aroundTilePosition = BWAPI::TilePosition(aroundPosition);
						//std::cout << "aroundTilePosition " << aroundTilePosition.x << "," << aroundTilePosition.y;

						if (aroundTilePosition.isValid()) {
							Cell & aroundCell = GetCell(aroundTilePosition);

							// 1000 프레임동안 얼마나 Damage 를 줄 수 있는가
							aroundCell.groundDamagePerSecond += (int)(groundWeaponType.damageAmount() * (1000 / groundWeaponType.damageCooldown()));
							// 질럿은 2배
							if (unit->getType() == BWAPI::UnitTypes::Protoss_Zealot) {
								aroundCell.groundDamagePerSecond += (int)(groundWeaponType.damageAmount() * (1000 / groundWeaponType.damageCooldown()));
							}
							//std::cout << " groundDamagePerSecond " << aroundCell.groundDamagePerSecond << std::endl;
						}
					}
				}
			}
		}
	}
}


void ConfidenceMap::Remove(BWAPI::Unit unit)
{
	if (unit != nullptr) {
		if (unit->getPlayer() == BWAPI::Broodwar->self()) {
			auto & List = GetCell(BWAPI::TilePosition(unit->getPosition())).myUnits;
			really_remove(List, unit);
		}
		else {
			auto & List = GetCell(BWAPI::TilePosition(unit->getPosition())).enemyUnits;
			really_remove(List, unit);
		}
	}

}


std::vector<BWAPI::Unit> ConfidenceMap::GetUnits(BWAPI::TilePosition topLeft, BWAPI::TilePosition bottomRight, BWAPI::Player player) const
{
	std::vector<BWAPI::Unit> Res;

	int i1, j1, i2, j2;
	std::tie(i1, j1) = GetCellCoords(topLeft);
	std::tie(i2, j2) = GetCellCoords(bottomRight);

	for (int j = j1; j <= j2; ++j) {
		for (int i = i1; i <= i2; ++i) {
			for (BWAPI::Unit unit : GetCell(i, j).Units) {
				if (unit->getPlayer() == player) {
					if (BWAPI_ext::inBoundingBox(BWAPI::TilePosition(unit->getPosition()), topLeft, bottomRight)) {
						Res.push_back(unit);
					}
				}
			}
		}
	}

	return Res;
}


int ConfidenceMap::getDangerLevelToFlyer(BWAPI::TilePosition from, BWAPI::TilePosition to)
{
	//std::cout << "getDangerLevelToFlyer from " << from.x << "," << from.y << " to " << to.x << "," << to.y << std::endl;
	
	if (from.isValid() == false || to.isValid() == false) {
		return 10000;
	}

	if (from.x == to.x && from.y == to.y) {
		return 0;
	}

	// from 에서 to 까지 가는 비행 경로상의 타일들을 pathTileList 에 넣는다
	std::set<BWAPI::TilePosition> pathTileList;

	int x_add_tile = 0;
	int y_add_tile = 0;

	int x_diff_tile = to.x - from.x;
	int y_diff_tile = to.y - from.y;


	if (x_diff_tile == 0) {
		// from 과 to 의 x 좌표가 같으면, y 축으로만 변화시키면서 타일을 더한다
		if (to.y > from.y) {
			y_add_tile = 1;
		}
		else {
			y_add_tile = -1;
		}

		for (int i = from.y; i != to.y; i += y_add_tile) {
			pathTileList.insert(BWAPI::TilePosition(from.x, i));
		}
	}
	else if (y_diff_tile == 0) {
		// from 과 to 의 y 좌표가 같으면, x 축으로만 변화시키면서 타일을 더한다
		if (to.x > from.x) {
			x_add_tile = 1;
		}
		else {
			x_add_tile = -1;
		}

		for (int i = from.x; i != to.x; i += x_add_tile) {
			pathTileList.insert(BWAPI::TilePosition(i, from.y));
		}
	}
	else {
		// from 과 to 의 x, y 좌표가 다르면, x축, y축으로 조금씩 변화시키면서 타일을 더한다
		BWAPI::Position fromPosition(from.x * TILE_SIZE + TILE_SIZE / 2, from.y * TILE_SIZE + TILE_SIZE / 2);
		BWAPI::Position toPosition(to.x * TILE_SIZE + TILE_SIZE / 2, to.y * TILE_SIZE + TILE_SIZE / 2);

		double x_diff_position = toPosition.x - fromPosition.x;
		double y_diff_position = toPosition.y - fromPosition.y;

		double x_add_position = 0;
		double y_add_position = 0;

		if (std::abs(x_diff_position) < std::abs(y_diff_position)) {

			x_add_position = (double)(x_diff_position / y_diff_position) * (double)(TILE_SIZE);
			y_add_position = TILE_SIZE;
		}
		else {
			x_add_position = TILE_SIZE;
			y_add_position = (double)(y_diff_position / x_diff_position) * (double)(TILE_SIZE);
		}

		if (x_diff_position > 0 && x_add_position < 0 || x_diff_position < 0 && x_add_position > 0) {
			x_add_position = x_add_position * (-1);
		}
		if (y_diff_position > 0 && y_add_position < 0 || y_diff_position < 0 && y_add_position > 0) {
			y_add_position = y_add_position * (-1);
		}


		int i = fromPosition.x;
		int j = fromPosition.y;
		while (true) {

			pathTileList.insert(BWAPI::TilePosition(i / TILE_SIZE, j / TILE_SIZE));

			//std::cout << "fromPosition " << fromPosition.x << "," << fromPosition.y
			//	<< " toPosition " << toPosition.x << ", " << toPosition.y
			//	<< " x_add_position " << x_add_position << " y_add_position " << y_add_position
			//	<< " insert " << i << "," << j << " (" << i / TILE_SIZE << "," << j / TILE_SIZE << ")" << std::endl;

			if (std::abs(toPosition.x - i) < std::abs(x_add_position)
				|| std::abs(toPosition.y - j) < std::abs(y_add_position))
			{
				break;
			}

			i += (int)x_add_position;
			j += (int)y_add_position;
		}
	}

	// pathTile에 대해, ConfidenceMap 에서 대공 DPS 의 합계를 계산한다
	int sum = 0;
	for (auto & currentTile : pathTileList) {
		if (currentTile.isValid()) {
			//BWAPI::Broodwar->drawBoxMap( currentTile.x * TILE_SIZE, currentTile.y * TILE_SIZE,  currentTile.x * TILE_SIZE + TILE_SIZE, currentTile.y * TILE_SIZE + TILE_SIZE, BWAPI::Colors::Green);
			Cell & currentCell = GetCell(currentTile);
			sum += currentCell.airDamagePerSecond;
		}
	}

	return sum;
}

