//==========================================================
// MemoryManager.cpp
//
// Implantation de la classe CMemoryManager
//
//==========================================================


//==========================================================
// En-têtes
//==========================================================
#include <MemoryManager/MemoryManager.h>
#include <iomanip>
#include <sstream>
#include <stdlib.h>

using namespace std;

MemoryManager::MemoryManager(){
	maxMemoryUsed = 0;
	currentMemoryUsed = 0;
	nbAllocation = 0;
	nbDesallocation = 0;

}

MemoryManager::~MemoryManager(){
	FILE * fichier = fopen("Memory report.txt","w");
	if (fichier != NULL){
		fprintf(fichier,"Memoire maximum utilisée : %d (soit ~%d Ko ou ~%d Mo)\n",maxMemoryUsed,maxMemoryUsed/1024,maxMemoryUsed/(1024*1024));
		fprintf(fichier,"Nombre d'allocations memoires : %d\n",nbAllocation);
		fprintf(fichier,"Nombre de desallocations memoire : %d\n",nbDesallocation);
		fprintf(fichier,"\n\nListe des allocations :\n\n");
		for(std::map<std::string,FileReport >::iterator it = fileAllocationReporter.begin(); it != fileAllocationReporter.end(); ++it){
			fprintf(fichier,"\n-----------------------------\n");
			if (it->second.maxSizeAllocated < 1024){
				fprintf(fichier,"%s :\t%d allocations => %d octets\n",it->first.c_str(),it->second.nbAllocations,it->second.maxSizeAllocated);
			}else if(it->second.maxSizeAllocated < 1024*1024){
				fprintf(fichier,"%s :\t%d allocations => %.2f Ko\n",it->first.c_str(),it->second.nbAllocations,it->second.maxSizeAllocated/1024.0);
			}else{
				fprintf(fichier,"%s :\t%d allocations => %.2f Mo\n",it->first.c_str(),it->second.nbAllocations,it->second.maxSizeAllocated/(1024.0*1024));
			}
			fprintf(fichier,"-----------------------------\n");
			for (std::map<int,int>::iterator ligneIterator = it->second.lignes.begin(); ligneIterator != it->second.lignes.end(); ++ligneIterator){
				fprintf(fichier,"\tligne %d :\t%d allocations\n",ligneIterator->first,ligneIterator->second);
			}
		}
		if (memoryHeap.empty()){
			fprintf(fichier,"\n\n\n#######################################\n");
			fprintf(fichier,"           Pas de leak memoire\n");
			fprintf(fichier,"#######################################\n");
		}else{
			int total = 0;
			fprintf(fichier,"\n\n\n#######################################\n");
			fprintf(fichier,"            %d leaks memoire\n",memoryHeap.size());
			fprintf(fichier,"#######################################\n");
			for (map<int,MemoryBlock>::iterator it = memoryHeap.begin(); it != memoryHeap.end(); ++it){
				if (it->second.size < 1024){
					fprintf(fichier,"%s : ligne %d\t\t size : %d octets\n",it->second.file.c_str(),it->second.line, it->second.size);
				}else if(it->second.size < (1024*1024)){
					fprintf(fichier,"%s : ligne %d\t\t size : %.2f Ko\n",it->second.file.c_str(),it->second.line, it->second.size/1024.0f);
				}else{
					fprintf(fichier,"%s : ligne %d\t\t size : %.2f Mo\n",it->second.file.c_str(),it->second.line, it->second.size/(1024.0f*1024));
				}
				total += it->second.size;
			}
			if (total < 1024){
				fprintf(fichier,"\n\nTotal : %d octets\n",total);
			}else if(total < 1024*1024){
				fprintf(fichier,"\n\nTotal : %.2f Ko\n",total/1024.0f);
			}else{
				fprintf(fichier,"\n\nTotal : %.2f Mo !!\n",total/(1024.0f*1024));
			}
		}
	}
}

MemoryManager& MemoryManager::getInstance(){
	static MemoryManager instance;
	return instance;
}

void* MemoryManager::allocation(int size,const string& file, int line,bool array){
	// gestion des statistiques :
	nbAllocation++;
	currentMemoryUsed += size;
	if (currentMemoryUsed > maxMemoryUsed){
		maxMemoryUsed = currentMemoryUsed;
	}
	
	MemoryBlock block;
	// on recupere le nom du fichier
    std::string::size_type Pos = file.find_last_of("\\/");
	if (Pos != std::string::npos){
        block.file = file.substr(Pos + 1, std::string::npos);
	}else{
        block.file = file;
	}

	block.line = line;
	block.size = size;
	block.array = array;
	fileAllocationReporter[block.file].lignes[block.line]++;
	fileAllocationReporter[block.file].nbAllocations ++;
	fileAllocationReporter[block.file].currentSizeAllocated+= size;
	if (fileAllocationReporter[block.file].currentSizeAllocated > fileAllocationReporter[block.file].maxSizeAllocated){
		fileAllocationReporter[block.file].maxSizeAllocated = fileAllocationReporter[block.file].currentSizeAllocated;
	}
	void * address = malloc(size);
	memoryHeap[(int)address] = block;
	return address;
}

void MemoryManager::desallocation(void * address,bool array){
	map<int,MemoryBlock>::iterator it = memoryHeap.find((int)address);
	if (it != memoryHeap.end()){
		if (it->second.array != array){
			fprintf(stderr,"Mauvais type de desalloaction : tableau vs scalaire\n");
			throw;
		}
		// gestion des statistiques
		nbDesallocation++;
		currentMemoryUsed -= it->second.size;
		fileAllocationReporter[it->second.file].currentSizeAllocated-=it->second.size;
		// on le supprime de la map.
		memoryHeap.erase(it);
	}
	free(address);
}
