#include "Flats.h"
#include <omp.h>
#include <iostream>
#include "taos.h"

Flats::Flats() {
	master = NULL;
}

Flats::Flats(string name) : Calibration(name) {
	master = NULL;
}

Flats::~Flats() {
	if (data) {
		std::cout << "Liberando Flats data ... ";
		free(data);
		data = NULL;
		std::cout << "Done" << std::endl;
	}
	if (master) {
		std::cout << "Liberando Flats Master ... ";
		free(master);
		master = NULL;
		std::cout << "Done" << std::endl;
	}
}

void Flats::ObtainMaster(long* biasmasterdata, long* darksmasterdata) {
	
	//Apartamos memoria para guardar la informacion de FLATS
	double* ptrmaster = master;
	ptrmaster = (double*)calloc(Xs * Ys, sizeof(double));
	long int* temp_median = (long int*)calloc(Xs * Ys, sizeof(long int));
	int xs = Xs;
	int ys = Ys;
	int xys = xs * ys;
	int frames = Frames;
	int row;
	int pixel;
	std::list<long> pixel_list;
	double mean;

	std::cout << "Flats Master ... ";

#pragma omp parallel for shared(biasmasterdata, darksmasterdata, xs, ys, frames, temp_median, xys) private(pixel_list, row, pixel) num_threads(THREADNUM)
	//Recorremos Y's
	for (int py = 0; py < ys; py++)
	{
		row = py * xs;

		//Recorremos X's
		for (int px = 0; px < xs; px++)
		{

			pixel = px + row;

			//Recorremos cada Frame para obtener la lista
			for (int frm = 0; frm < frames; frm++)
			{
				//Guardamos el dato del frame en una lista
				pixel_list.push_back((data[pixel + (frm * xys)] -
					biasmasterdata[pixel] - darksmasterdata[pixel]));
			}

			//Ordenamos la lista para sacar la mediana
			pixel_list.sort();
			temp_median[pixel] = getMedian(pixel_list);

			//Una vez que tenemos la mediana agregada limpiamos la lista para continuar con el siguiente pixel
			pixel_list.clear();
		}
	}

	for (int i = 0; i < xys; i++)
	{
		pixel_list.push_back(temp_median[i]);
	}

	mean = getMean(pixel_list);

#pragma omp parallel for shared(ptrmaster, temp_median, xs, ys) private(row, pixel) num_threads(THREADNUM)
	//Recorremos Y's
	for (int py = 0; py < ys; py++)
	{

		row = py * xs;

		//Recorremos X's
		for (int px = 0; px < xs; px++)
		{
			pixel = px + row;

			ptrmaster[pixel] = temp_median[pixel] / mean;
			//*(ptrmaster + px + (py * xs)) = *(temp_median + px + (py * xs)) / mean; 
		}
	}

	free(temp_median);

	master = ptrmaster;
	ptrmaster = NULL;

	std::cout << "Done" << std::endl;
}

double* Flats::getMaster() {
	//Regresa un puntero hacia MASTER BIAS, que es donde se encuentra toda la informacion del archivo MASTER BIAS
	return master;
}

void Flats::free_data(){
	if (data) {
		std::cout << "Liberando FLATS data ... ";
		free(data);
		data = NULL;
		std::cout << "Done" << std::endl;
	}
}

void Flats::free_Master() {
	if (master) {
		std::cout << "Liberando FLATS MASTER ... ";
		free(master);
		master = NULL;
		std::cout << "Done" << std::endl;
	}
}

double Flats::getMean(list<long> plist) {

	list<long>::iterator it = plist.begin();
	double sum = 0;

	for (it; it != plist.end(); it++)
	{
		sum = sum + *it;
	}

	double r = sum / plist.size();

	return r;
}

void Flats::PrintMasterData(int limx, int limy) {

	std::cout << "\t" << "Master Data \n";
	std::cout << "\t" << filename << "\n";

	//Recorremos Y's
	for (int py = 0; py < limy; py++)
	{
		//Recorremos X's
		for (int px = 0; px < limx; px++)
		{
			std::cout << "[" << px << "]" << "[" << py << "]" << "[1]:" << master[px + (py * Xs)] << "\n";
		}
	}

	std::cout << "\n\n";
}

//Lectura de archivo master
void Flats::setMaster(string masterfile) {

	std::cout << "Lectura de Masterfile Flats " << masterfile;

	//Conversion de string a char[] para funcion de fits_open_file
	char* file = new char[masterfile.size() + 1];
	masterfile.copy(file, masterfile.size() + 1);
	file[masterfile.size()] = '\0';

	//Abrimos el archivo
	if (fits_open_file(&fptr, file, READONLY, &status)) {
		fits_report_error(stderr, this->status); //Imprime error de status
		exit(EXIT_FAILURE);
	}

	//Declaramos desde donde queremos comenzar a leer la imagen en fpixel:
	long fpixel[] = { 1, 1, 1 };

	//Debemos obtener el total de pixeles que contiene la imagen y la cantidad de frames del archivo
	long int nelements = Xs;	//Pixeles a leer por renglon

	master = (double*)calloc(Xs * Ys, sizeof(double));
	double* tempdata = (double*)calloc(nelements, sizeof(double));

	//nulval se de clara como short -1 y anynul se trabaja con un int 0, datatype para leer TULONG
	short nulval = -1;

	//Recorremos cada renglon
	for (int j = 1; j <= nelements; j++)
	{
		fpixel[1] = j;

		if (fits_read_pix(this->fptr, TDOUBLE, fpixel, nelements, &nulval, tempdata, 0, &this->status)) {
			fits_report_error(stderr, this->status); //Imprime error de status
			exit(EXIT_FAILURE);
		}

		for (int k = 0; k < nelements; k++)
		{
			*(master + ((j - 1) * nelements) + k) = tempdata[k];
		}
	}

	//Cerramos el archivo
	if (fits_close_file(fptr, &status)) {
		fits_report_error(stderr, this->status); //Imprime error de status
		exit(EXIT_FAILURE);
	}

	free(tempdata);
	delete file;

	std::cout << "Done \n\n";
}

void Flats::setNewFlats(string newflats) {
	if (master) {
		free(master);
		master = NULL;
	}

	Calibration::setNewFile(newflats);
}

void Flats::setNullMaster(int xs, int ys) {

	int indx;
	master = (double*)calloc(xs * ys, sizeof(double));

	for (int py = 0; py < ys; py++) {

		indx = py * xs;

		for (int px = 0; px < xs; px++) {
			master[px + indx] = 1;
		}
	}

}