#include "CFremenGrid.h"
#include <string>
#include <vector>

using namespace std;
static bool quick = false;

CFremenGrid::CFremenGrid(const char* name,float spatialCellSize,int temporalCellSize,const char* model,int order)
{
	/*load grid info*/
	FILE* file=fopen(name,"r");
	long int minTime,maxTime;
	float minX,maxX,minY,maxY;
	fscanf(file,"%ld %ld\n",&minTime,&maxTime);
	fscanf(file,"%f %f\n",&minX,&maxX);
	fscanf(file,"%f %f\n",&minY,&maxY);
	set(minTime,minX,minY,(int)((maxTime-minTime)/temporalCellSize),(int)((maxX-minX)/spatialCellSize),(int)((maxY-minY)/spatialCellSize),spatialCellSize,temporalCellSize);

	/*fill the grid with data*/
	long int time;
	float x,y;

	temporalArray = nullptr;
	cem_model = nullptr;
	nn_model = nullptr;

	if (std::string(model) == "HyT-CEM") {
		cem_model = new CExpectation(order);

		while (feof(file)==0)
		{
			fscanf(file,"%ld %f %f\n",&time,&x,&y);
			cem_model->add_v(x, y, time);
			histogram[getCellIndex(time,x,y)]++;
			hasData[getFrelementIndex(x,y)]++;
		}
		fclose(file);
	} else if (std::string(model) == "Neural") {
		nn_model = new CNeural(order, xDim*yDim);

		while (feof(file)==0)
		{
			fscanf(file,"%ld %f %f\n",&time,&x,&y);
			histogram[getCellIndex(time,x,y)]++;
			hasData[getFrelementIndex(x,y)]++;
		}
		fclose(file);
		for (int t = 0; t < tDim; ++t) {
			std::vector<double> v;
			for (int i = 0; i < xDim*yDim; ++i) {
				v.push_back(histogram[t*xDim*yDim + i]);
			}
			nn_model->add_v(t*temporalResolution + oT, v);
		}
	} else {
		temporalArray = (CTemporal**) calloc(10000000,sizeof(CTemporal));

		/*spawnModels*/
		for (int i = 0;i<numFrelements;i++) temporalArray[i] = spawnTemporalModel(model,86400*7,order,1);

		while (feof(file)==0)
		{
			fscanf(file,"%ld %f %f\n",&time,&x,&y);
			histogram[getCellIndex(time,x,y)]++;
			hasData[getFrelementIndex(x,y)]++;
		}
		fclose(file);
	}
}

CFremenGrid::CFremenGrid(long int originT,float originX,float originY,int dimT,int dimX,int dimY,float spatialCellSize,int temporalCellSize)
{
	set(originT,originX,originY,dimT,dimX,dimY,spatialCellSize,temporalCellSize);
}

void CFremenGrid::set(long int originT,float originX,float originY,int dimT,int dimX,int dimY,float spatialCellSize,int temporalCellSize)
{
	lastTimeStamp = 0;
	minProb = 0.05;
	maxProb = 1-minProb;
	debug = false;
	oT = originT;
	oX = originX;
	oY = originY;
	tDim = dimT;
	xDim = dimX;
	yDim = dimY;
	printf("Dim: %i %i %i\n",dimX,dimY,dimT);
	spatialResolution = spatialCellSize;
	temporalResolution = temporalCellSize;
	numFrelements = yDim*xDim;
	numCells = tDim*yDim*xDim;
	probs = (float*) malloc(numCells*sizeof(float));
	histogram = (int*) malloc(numCells*sizeof(int));
	hasData = (int*) malloc(numFrelements*sizeof(int));
	memset(histogram,0,numCells*sizeof(int));
}

CFremenGrid::~CFremenGrid()
{
	free(histogram);
	if (cem_model) {
		delete cem_model;
	} else if (nn_model) {
		delete nn_model;
	} else {
		for (int i = 0;i<numFrelements;i++) free(temporalArray[i]);
	}
}

int CFremenGrid::generateFromData(long int *time, float *x,float *y,int number)
{
	int cellIndex = 0;
	for (int i = 0;i<number;i++)
	{
		cellIndex = getCellIndex(time[i],x[i],y[i]);
		histogram[cellIndex]++; 
	}
	return 0;
}

void CFremenGrid::display(bool verbose)
{
	for (int s = 0;s<numFrelements;s++)
	{
		int i = 0;
		for (int t = 0;t<tDim;t++){
			i = t*xDim*yDim+s;
			if (s == 63 || quick == false) {
				printf("AA: %.3f %i\n",probs[i],histogram[i]);
			}
		}
	}
}

void CFremenGrid::update(int order)
{
	if (cem_model) {
		cem_model->update(order);
	} else if (nn_model) {
		nn_model->update(order);
	} else {
		for (int s = 0;s<numFrelements;s++)
		{
			if (s == 63 || quick == false){
				for (int t = 0;t<tDim;t++) temporalArray[s]->add(t*temporalResolution+oT,histogram[t*xDim*yDim+s]);
				temporalArray[s]->update(order);
				float sumProb = 0;
				float sumHist = 0;
				int index = 0;
				for (int t = 0;t<tDim;t++){
					 index = t*xDim*yDim+s;
					 sumProb += temporalArray[s]->predict(t*temporalResolution+oT);
					 sumHist += histogram[index];
				}
				if (sumProb > 0) temporalArray[s]->correction = sumHist/sumProb;
				//printf("Corr: %f\n",temporalArray[s]->correction);
			}
		}
	}
}

float CFremenGrid::computeError(int order)
{
	events = 0;
	float error = 0;
	FILE* file = fopen("error.pgm","w");
	fprintf(file,"P2\n%i %i\n %i\n",xDim,yDim,255);
	float cellError[numFrelements];
	float maxCellError = 0;

	for (int s = 0;s<numFrelements;s++){
		cellError[s] = 0;
		if (s == 63 || quick == false){
			for (int t = 0;t<tDim;t++){
				int i = t*xDim*yDim+s;
				//if (hasData[s] > 0){
					events++;
					cellError[s] += pow(probs[i]-histogram[i],2);
				//}
//					events += histogram[i];
				//}
			}
		}
		//if (i%xDim == 0 && i != 0) fprintf(file,"\n");
		error+=cellError[s];
		//maxCellError = max(maxCellError,cellErrors[s]);
	}
	/*for (int s = 0;s<numFrelements;s++)
	{
		printf("%.0f ",cellError[s]);
		if ((s+1)%xDim==0) printf("\n");
	}*/
	//fprintf(file,"%i  ",(int)(cellError*255/maxHist));
	fclose(file);
	return sqrt(error);
}

/*void CFremenGrid::drawError(long int time)
{
	float maxHist = 0;
	char filename[10];
	sprintf(filename,"%05ld.pgm",time);
	FILE* file = fopen(filename,"w");
	for (int i = 0;i<numFrelements;i++) maxHist = max(probs[i],maxHist);
	fprintf(file,"P2\n#pic at time %ld\n%i %i\n %i\n",time,xDim,yDim,255);
	for (int i = 0;i<numFrelements;i++){
		 if (i%xDim == 0 && i != 0) fprintf(file,"\n");
		 fprintf(file,"%i  ",(int)(probs[i+xDim*yDim*time]*255/maxHist));
	}
	fclose(file);
}*/



int CFremenGrid::generateFromModel(int order,CFremenGrid *grid)
{
	if (grid == NULL) grid = this;
	grid->update(order);
	const float size = spatialResolution*spatialResolution*(temporalResolution/86400*2*M_PI);
	if (cem_model) {
		for (int t = 0; t < tDim ; ++t) {
			//std::vector<float> prediction = grid->cem_model->estimate_v(t*temporalResolution + oT);
			for (int s = 0; s < numFrelements; ++s) {
				int x = s % xDim;
				int y = s / xDim;
				float x1 = x*spatialResolution + oX;
				float x2 = (x+1)*spatialResolution + oX;
				float y1 = y*spatialResolution + oY;
				float y2 = (y+1)*spatialResolution + oY;
				float t1 = t*temporalResolution + oT;
				float t2 = (t+1)*temporalResolution + oT;
				probs[t*xDim*yDim+s] = (grid->cem_model->estimate_v(x1, y1, t1) + grid->cem_model->estimate_v(x2, y1, t1) + grid->cem_model->estimate_v(x1, y2, t1) + grid->cem_model->estimate_v(x2, y2, t1) + grid->cem_model->estimate_v(x1, y1, t2) + grid->cem_model->estimate_v(x2, y1, t2) + grid->cem_model->estimate_v(x1, y2, t2) + grid->cem_model->estimate_v(x2, y2, t2)) * size / 8;
			}
		}
	} else if (nn_model) {
		for (int t = 0; t < tDim ; ++t) {
			std::vector<float> prediction = grid->nn_model->estimate_v(t*temporalResolution + oT);
			for (int i = 0; i < xDim*yDim; ++i) {
				probs[t*xDim*yDim + i] = prediction[i];
			}
		}
	} else {
		for (int s = 0;s<numFrelements;s++)
		{
			if (s == 63 || quick == false){
				for (int t = 0;t<tDim;t++) probs[t*xDim*yDim+s] = grid->temporalArray[s]->correction*grid->temporalArray[s]->predict(t*temporalResolution+oT);
			}
			if (s ==63) grid->temporalArray[s]->print();
		}
	}
	return 0;
}


void CFremenGrid::drawTime(long int time)
{
	float maxHist = 0;
	char filename[10];
	sprintf(filename,"%05ld.pgm",time);
	FILE* file = fopen(filename,"w");
	for (int i = 0;i<numCells;i++) maxHist = max(probs[i],maxHist);
	fprintf(file,"P2\n#pic at time %ld\n%i %i\n %i\n",time,xDim,yDim,255);
	for (int i = 0;i<numFrelements;i++){
		 if (i%xDim == 0 && i != 0) fprintf(file,"\n");
		 fprintf(file,"%i  ",(int)(probs[i+xDim*yDim*time]*255/maxHist));
	}
	fclose(file);
}


int CFremenGrid::getFrelementIndex(float  x,float  y)
{
	int iX,iY;
	iX = (int)((x-oX)/spatialResolution);
	iY = (int)((y-oY)/spatialResolution);
	if (iX < xDim && iY < yDim && iX >= 0 && iY >=0) return iX+xDim*iY;
	return 0;
}

int CFremenGrid::getCellIndex(long int t,float  x,float  y)
{
	int iX,iY,iT;
	iT = (int)((t-oT)/temporalResolution);
	iX = (int)((x-oX)/spatialResolution);
	iY = (int)((y-oY)/spatialResolution);
	if (iX < xDim && iY < yDim && iT < tDim && iX >= 0 && iY >=0 && iT >= 0) return iX+xDim*(iY+yDim*iT);
	return 0;
}

void CFremenGrid::save(const char* filename,bool lossy,int forceOrder)
{
	FILE* f=fopen(filename,"w");
	fwrite(&xDim,sizeof(int),1,f);
	fwrite(&yDim,sizeof(int),1,f);
	fwrite(&tDim,sizeof(long int),1,f);
	fwrite(&oX,sizeof(float),1,f);
	fwrite(&oY,sizeof(float),1,f);
	fwrite(&oT,sizeof(long int),1,f);
	fwrite(&spatialResolution,sizeof(float),1,f);
	fwrite(&temporalResolution,sizeof(float),1,f);
	fwrite(probs,sizeof(float),numCells,f);
	bool is_cem = cem_model;
	bool is_nn = nn_model;
	fwrite(&is_cem,sizeof(bool),1,f);
	fwrite(&is_nn,sizeof(bool),1,f);
	if (is_cem) {
		cem_model->save(f,false);
	} else if (is_nn) {
		nn_model->save(f,false);
	} else {
		for (int i=0;i<numCells;i++) temporalArray[i]->save(f,false);
	}
	fclose(f);
}

bool CFremenGrid::load(const char* filename)
{
	int ret = 0;
	FILE* f=fopen(filename,"r");
	if (f == NULL){
		printf("FrOctomap %s not found, aborting load.\n",filename);
		return false;
	}
	ret += fread(&xDim,sizeof(int),1,f);
	ret += fread(&yDim,sizeof(int),1,f);
	ret += fread(&tDim,sizeof(long int),1,f);
	ret += fread(&oX,sizeof(float),1,f);
	ret += fread(&oY,sizeof(float),1,f);
	ret += fread(&oT,sizeof(long int),1,f);
	ret += fread(&spatialResolution,sizeof(float),1,f);
	ret += fread(&temporalResolution,sizeof(float),1,f);
	numCells = xDim*yDim*tDim;
	numFrelements = xDim*yDim;
	ret += fread(probs,sizeof(float),numCells,f);
	bool is_cem;
	bool is_nn;
	fread(&is_cem,sizeof(bool),1,f);
	fread(&is_nn,sizeof(bool),1,f);
	if (is_cem) {
		cem_model = new CExpectation(0);
		cem_model->load(f);
	} else if (is_nn) {
		nn_model = new CNeural(0, xDim*yDim);
		nn_model->load(f);
	} else {
		for (int i=0;i<numFrelements;i++) temporalArray[i]->load(f);
	}
	/*cellArray = (CFrelement**) malloc(numCells*sizeof(CFrelement*));
	for (int i=0;i<numCells;i++) cellArray[i] = new CFrelement();
	for (int i=0;i<numCells;i++){
		cellArray[i]->load(f);
		cellArray[i]->signalLength = signalLength;
	}*/
	printf("Loaded a grid of %i bytes with %i cells as %ix%ix%i.\n",ret,numCells,tDim,xDim,yDim);
	fclose(f);
	//update();
	return true;
}

void CFremenGrid::print(bool verbose)
{
//	for (int i = 0;i<numCells;i++) printf("Histo: %i\n",histogram[i]);
// temporalArray[i]->print(verbose);
		//if (frelements[i].periodicities > 0)
		//{
			//printf("Cell: %i %i ",i,frelements[i].periodicities);
		//}
}
