#include "State.h"


State::State(){ hp = 0; hpEnemies = 0; allies = 0; enemies = 0; distance = 0; dps = 0; dpsEnemies = 0; coolDown = 0; attacked = 0; speed = 0; }
State::State(int hp, int hpEnemies, int allies, int enemies, int distance, int dps, int dpsEnemies, int coolDown, int attacked, int speed) : hp(hp), hpEnemies(hpEnemies), allies(allies), enemies(enemies), distance(distance), dps(dps), dpsEnemies(dpsEnemies), coolDown(coolDown), attacked(attacked), speed(speed) {}

bool State::operator==(const State &state) const {
	return (hp == state.hp && hpEnemies == state.hpEnemies && allies == state.allies && enemies == state.enemies && distance == state.distance && dps == state.dps && dpsEnemies == state.dpsEnemies && coolDown == state.coolDown && attacked == state.attacked && speed == state.speed);
}

std::ostream& operator<<(std::ostream &strm, const State &state){
	return strm << "HP: " << state.hp << " HP Enemies:" << state.hpEnemies << " Nr. of Allies:" << state.allies << " Nr. of Enemies:" << state.enemies << " Distance:" << state.distance << " dps:" << state.dps << " Enemies dps:" << state.dpsEnemies << " Weapon cool down:" << state.coolDown << " Under Attack?" << state.attacked << " Speed lvl:" << state.speed;
}

size_t StateHash::operator()(const State &state) const {
	return std::hash<int>()(state.hp) ^ std::hash<int>()(state.hpEnemies) ^ std::hash<int>()(state.allies) ^ std::hash<int>()(state.enemies) ^ std::hash<int>()(state.distance) ^ std::hash<int>()(state.dps) ^ std::hash<int>()(state.dpsEnemies) ^ std::hash<int>()(state.coolDown) ^ std::hash<int>()(state.attacked) ^ std::hash<int>()(state.speed);
}

/* Getters */

int State::getHP(){
	return hp;
}
int State::getHPEnemies(){
	return hpEnemies;
}
int State::getAllies(){
	return allies;
}
int State::getEnemies(){
	return enemies;
}
int State::getDistance(){
	return distance;
}
int State::getDPS(){
	return dps;
}
int State::getDPSEnemies(){
	return dpsEnemies;
}
int State::getCoolDown(){
	return coolDown;
}

int State::getAttacked(){
	return attacked;
}

int State::getSpeed(){
	return speed;
}

/* Utils */

int State::getEnemiesThreshold(int friends, int foes){
	if (foes < 1) return NONE;
	if (foes / friends > 0){
		if (foes > 2 && foes / friends > 1)	return LARGE;
		return MEDIUM;
	}
	return SMALL;
}

int State::getAlliesThreshold(int friends, int foes){
	if (foes < 1) return SMALL;
	if (friends / foes > 0){
		if (friends > 2 && friends / foes > 1)	return LARGE;
		return MEDIUM;
	}
	return SMALL;
}

int State::getHPThreshold(int hp){
	if (hp > 140) return HIGH;
	if (hp > 60) return NORMAL;
	if (hp > 20) return LOW;
	return CRITICAL;
}

int State::getHPEnemies(BWAPI::Unitset foes){
	int hp = 0;
	for (auto &foe : foes){
		hp += foe->getHitPoints();
	}
	return hp;
}

int State::getHPEnemiesThreshold(BWAPI::Unitset foes, int ownUnits){
	int hp = 0;
	for (auto &foe : foes){
		hp += foe->getHitPoints();
	}
	if (hp / ownUnits > 140) return HIGH;
	if (hp / ownUnits > 60) return NORMAL;
	if (hp / ownUnits > 20) return LOW;
	return CRITICAL;
}

int State::getEnemyDistanceThreshold(int dist, BWAPI::Unit u){
	if (dist <= u->getType().sightRange() / 2) return CLOSE;
	if (dist <= u->getType().groundWeapon().maxRange()) return MEDIUM;
	return FARTHER;
}

int State::getClosestEnemyDistance(BWAPI::Unitset foes, BWAPI::Unit u){
	if (foes.empty()) return 0;
	int distance = 9999;
	for (auto &foe : foes){
		if (u->getDistance(foe) < distance)
			distance = u->getDistance(foe);
	}
	return distance;

}

int State::calculateDPS(BWAPI::Unit unit){
	//return unit->getType().maxGroundHits() *  Broodwar->getLatencyFrames() / unit->getGroundWeaponCooldown(); // epic fail! non of the methods were correct.

	if (unit->getType().groundWeapon() == BWAPI::WeaponTypes::None) return 0;
	else return unit->getType().groundWeapon().damageAmount() *  BWAPI::Broodwar->getAverageFPS() / unit->getType().groundWeapon().damageCooldown();
}

int State::calculateDPS(BWAPI::Unitset units){
	int dps = 0;
	for (auto &unit : units){
		if (unit->getType().groundWeapon() != BWAPI::WeaponTypes::None)
			dps += unit->getType().groundWeapon().damageAmount() *  BWAPI::Broodwar->getAverageFPS() / unit->getType().groundWeapon().damageCooldown();
	}
	return dps;
}

int State::getDPSThreshold(int dps){
	if (dps < 8) return CRITICAL;
	if (dps < 16) return LOW;
	if (dps < 32) return NORMAL;
	return HIGH;
}

int State::getSpeedThreshold(BWAPI::Unit unit){
	if (unit->getType().topSpeed() <= 4) return SLOW;
	if (unit->getType().topSpeed() <= 6) return MEDIUM;
	return FAST;
}
