#include "GroundArmyInfo.h"

#include "MemoryManager/debugNew.h"

using namespace std;
using namespace ground;

#define MIN(a,b) a<b?a:b
#define MAX(a,b) a>b?a:b

GroundArmyInfo::GroundArmyInfo(void){
	mapWidth = 0;
	mapHeight = 0;
	visibilityMatrix = NULL;
	dynamicDanger = NULL;
	lastUpdateTime = NULL;
	updateCounter = 4000;

}

GroundArmyInfo::~GroundArmyInfo(void){
	if (visibilityMatrix != NULL){
		for (int i = 0; i < mapWidth; i++){
			delete[] visibilityMatrix[i];
		}
		delete[] visibilityMatrix;
		visibilityMatrix = NULL;
	}
	if (dynamicDanger != NULL){
		for (int i = 0; i < mapWidth; i++){
			delete[] dynamicDanger[i];
		}
		delete[] dynamicDanger;
		dynamicDanger = NULL;
	}
	if (lastUpdateTime != NULL){
		for (int i = 0; i < mapWidth; i++){
			delete[] lastUpdateTime[i];
		}
		delete[] lastUpdateTime;
		lastUpdateTime = NULL;
	}
}

void GroundArmyInfo::addUnit(Unit* unit){
	if (!containUnit(unit)){
		visibilityArea.push_back(unit);
	}
}

void GroundArmyInfo::removeUnit(Unit* unit){
	for(vector<VisibilityArea*>::iterator it = visibilityArea.begin(); it != visibilityArea.end(); ++it){
		if(*it == unit){
			visibilityArea.erase(it);
			return;
		}
	}
}

bool GroundArmyInfo::containUnit(Unit* unit){
	for(vector<VisibilityArea*>::iterator it = visibilityArea.begin(); it != visibilityArea.end(); ++it){
		if(*it == unit){
			return true;
		}
	}
	return false;
}

void GroundArmyInfo::removeAllUnits(){
	visibilityArea.clear();
}

void GroundArmyInfo::addDangerGenerator(DangerGenerator* generator){
	dangerGenerators[generator] = generator;
}

void GroundArmyInfo::removeDangerGenerator(DangerGenerator* generator){
	map<void*,DangerGenerator*>::iterator it = dangerGenerators.find(generator);
	if (it != dangerGenerators.end()){
		dangerGenerators.erase(it);
	}
}

bool GroundArmyInfo::containDangerGenerator(DangerGenerator* generator){
	return dangerGenerators.find(generator) != dangerGenerators.end();
}

void GroundArmyInfo::removeAllDangerGenerator(){
	dangerGenerators.clear();
}

bool** GroundArmyInfo::getVisibilityMatrix(){
	return visibilityMatrix;
}

int GroundArmyInfo::getMapWidth(){
	return mapWidth;
}

int GroundArmyInfo::getMapHeight(){
	return mapHeight;
}

float** GroundArmyInfo::getDynamicDanger(){
	return dynamicDanger;
}

int** GroundArmyInfo::getLastUpdateTime(){
	return lastUpdateTime;
}

void GroundArmyInfo::setupMapInfo(int width, int height){
	if (visibilityMatrix != NULL){
		for (int i = 0; i < mapWidth; i++){
			delete[] visibilityMatrix[i];
		}
		delete[] visibilityMatrix;
		visibilityMatrix = NULL;
	}
	if (dynamicDanger != NULL){
		for (int i = 0; i < mapWidth; i++){
			delete[] dynamicDanger[i];
		}
		delete[] dynamicDanger;
		dynamicDanger = NULL;
	}
	if (lastUpdateTime != NULL){
		for (int i = 0; i < mapWidth; i++){
			delete[] lastUpdateTime[i];
		}
		delete[] lastUpdateTime;
		lastUpdateTime = NULL;
	}
	mapWidth = width;
	mapHeight = height;
	visibilityMatrix = new bool*[mapWidth];
	for(int i = 0; i < mapWidth; i++){
		visibilityMatrix[i] = new bool[mapHeight];
		for (int j = 0; j < mapHeight; j++){
			visibilityMatrix[i][j] = false;
		}
	}
	dynamicDanger = new float*[mapWidth];
	for(int i = 0; i < mapWidth; i++){
		dynamicDanger[i] = new float[mapHeight];
		for (int j = 0; j < mapHeight; j++){
			dynamicDanger[i][j] = 0.0f;
		}
	}
	lastUpdateTime = new int*[mapWidth];
	for(int i = 0; i < mapWidth; i++){
		lastUpdateTime[i] = new int[mapHeight];
		for (int j = 0; j < mapHeight; j++){
			lastUpdateTime[i][j] = 0;
		}
	}
	updateCounter = 4000;
	visibilityChecker.setMatrixParameters(width,height);
}

void GroundArmyInfo::update(){
	updateCounter++;
	visibilityChecker.checkVisibility(visibilityMatrix,visibilityArea);
	computeDynamicDanger();
	updateTimeMatrix();
}

void GroundArmyInfo::computeDynamicDanger(){
	map<void*,DangerGenerator*>::iterator it;
	float dangerLevel;
	float radius;
	float dist;
	float factor;

	float xmin,xmax,ymin,ymax;

	for(it = dangerGenerators.begin(); it!=dangerGenerators.end(); ++it) {
		Point2D& pos = it->second->getDangerPosition();
		// on verifie que le generateur de danger est bien visible (on ne va pas supposer dangereuse une zone
		// ou on ne voit pas d'ennemies
		if (visibilityMatrix[(int)pos.getX()][(int)pos.getY()]){
			radius = it->second->getDangerRadius();
			radius *= radius;	//radius au carré évite d'utiliser sqrt par la suite
			dangerLevel = it->second->getDangerLevel();

			//calcul des bornes de la zone modifier
			xmin = pos.getX()-radius;
			xmax = pos.getX()+radius;
			ymin = pos.getY()-radius;
			ymax = pos.getY()+radius;
			//vérification des bornes de la zone modifier
			if(xmin<0) xmin=0;
			if(xmax>=mapWidth) xmax=(float)(mapWidth-1);
			if(ymin<0) ymin=0;
			if(ymax>=mapHeight) ymax=(float)(mapHeight-1);

			//le calcul du danger
			for(float i=xmin; i<xmax; i++){
				for(float j=ymin; j<ymax; j++){
					dist = ((pos.getX()+0.5f)-i)*((pos.getX()+0.5f)-i) + ((pos.getY()+0.5f)-j)*((pos.getY()+0.5f)-j);
					factor = (radius-dist)/radius;
					if(factor>0){
						dynamicDanger[(int)i][(int)j] += dangerLevel*factor;
					}
				}
			}
		}
	}
}

void GroundArmyInfo::updateTimeMatrix(){
	// pour chaque case visible, on lui met le lastTime a updateCounter
	for (int i = 0; i < mapWidth; i++){
		for (int j = 0; j < mapHeight; j++){
			if (visibilityMatrix[i][j]){
				lastUpdateTime[i][j] = updateCounter;
			}
		}
	}
};

GroundArmyInfo::VisibilityAreaInfo GroundArmyInfo::getTimeAverage(Point2D position,int radius){
	VisibilityAreaInfo ret;
	ret.average = 0.0f;
	ret.caseCount = 0;
	int minX = (int)(MAX(position.getX() - radius,0.0f));
	int minY = (int)(MAX(position.getY() - radius,0.0f));
	int maxX = (int)(MIN(position.getX() + radius,mapWidth));
	int maxY = (int)(MIN(position.getY() + radius,mapHeight));
	for (int i = minX; i < maxX; i++){
		for (int j = minY; j < maxY; j++){
			if (!visibilityMatrix[i][j] && GroundMap::getInstance().getCase(i,j)->isPassable()){
				ret.average += updateCounter - lastUpdateTime[i][j];
				ret.caseCount++;
			}
		}
	}
	ret.average /= ret.caseCount;
	return ret;
}

Point2D GroundArmyInfo::getOldestCase(Point2D center, int radius){
	int minX = (int)(MAX(center.getX() - radius,0.0f));
	int minY = (int)(MAX(center.getY() - radius,0.0f));
	int maxX = (int)(MIN(center.getX() + radius,mapWidth));
	int maxY = (int)(MIN(center.getY() + radius,mapHeight));
	std::vector<Point2D> oldestVector;
	oldestVector.reserve(1000);
	int oldestTime = 999999;
	for (int i = minX; i < maxX; i++){
		for (int j = minY; j < maxY; j++){
			if (GroundMap::getInstance().getCase(i,j)->isPassable()){
				if(lastUpdateTime[i][j] < oldestTime){
					oldestVector.clear();
					oldestVector.push_back(Point2D(i,j));
					oldestTime = lastUpdateTime[i][j];
					//return Point2D(i,j);
				}else if(lastUpdateTime[i][j] == oldestTime){
					// on ajoute la case au vecteur
					if (oldestVector.size() > 5){
						if(rand()%100 < 25){
							oldestVector.push_back(Point2D(i,j));
						}
					}else{
						oldestVector.push_back(Point2D(i,j));
					}
				}
			}
		}
	}
	if(oldestVector.empty()){
		return Point2D(0,0);
	}
	int index = rand() % oldestVector.size();
	return oldestVector[index];
}

bool GroundArmyInfo::isVisible(const Point2D& pos){
	return isVisible((int)(pos.getX()+0.5f),(int)(pos.getY()+0.5f));
}

bool GroundArmyInfo::isVisible(int x, int y){
	if (x < 0 || x >= mapWidth || y < 0 || y >= mapHeight){
		return false;
	}else{
		return visibilityMatrix[x][y];
	}
}
float GroundArmyInfo::getDynamicDanger(const Point2D& pos){
	return getDynamicDanger((int)(pos.getX()+0.5f),(int)(pos.getY()+0.5f));
}

float GroundArmyInfo::getDynamicDanger(int x, int y){
	if (x < 0 || x >= mapWidth || y < 0 || y >= mapHeight){
		return false;
	}else{
		return dynamicDanger[x][y];
	}
}

