// TAOS-2.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <iostream>
#include <stdio.h>
#include <omp.h>
#include <fstream>
#include <string>
#include <list>
#include <iterator>

#include <fitsio.h>

//Libreria de taos
#include "taos.h"

//Objetos
#include "Bias.h"
#include "Darks.h"
#include "Flats.h"
#include "Science.h"
#include "Star.h"

//Procesos
#include "reading.h"
#include "Binarization.h"
#include "StarSearch.h"
#include "StarTrace.h"
#include "writing.h"
#include "Photometry.h"


vector<string> create_filenames(string fits_name, vector<Star> stars);
void WriteFitsImage(string filename, int naxis, long X, long Y, long int* data);
bool Veryfication(long X, long Y, long int* data);

using namespace std;

int main()
{

	std::cout << "\tCargamos archivo de configuracion\n" << std::endl;

#pragma region Lectura de configuracion

	ifstream txt;
	string str;
	string last_str;

	int cosum_frames = 5;
	int max_pct = 50;
	int radius_pct = 3;
	int lz = 1;

	txt.open(string(FILE_PATH + string(CONF_TXT)).c_str(), std::ios::in);

	if (txt.is_open()) {

		while (!txt.eof()) {

			txt >> str;

			if (last_str == "FRAMES:") {
				cosum_frames = atoi(str.c_str());
			}
			else if (last_str == "PCT:") {
				max_pct = atoi(str.c_str());
			}
			else if (last_str == "RADIUSPCT:") {
				radius_pct = atoi(str.c_str());
			}
			else if (last_str == "LZ:") {
				lz = atoi(str.c_str());
			}

			last_str = str;
			str = string();

		}

		std::cout << "Frames de cosuma: " << cosum_frames << std::endl;
		std::cout << "Porcentaje de valor maximo: " << max_pct << std::endl;
		std::cout << "Porcentaje permisible de cercania entre estrellas: " << radius_pct << std::endl;
		std::cout << "LZ: " << lz << "\n" << std::endl;

	}

#pragma endregion


	std::cout << "\tLectura de archivos de texto\n" << std::endl;

#pragma region Lectura de txt
	list<list<string>> txtfiles = reading();
	list<list<string>>::iterator litr = txtfiles.begin();
	list<string> Biastxt = *litr;
	litr++;
	list<string> Darkstxt = *litr;
	litr++;
	list<string> Flatstxt = *litr;
	litr++;
	list<string> FitsFileList = *litr;

	string biasfilename;
	string darksfilename;
	string flatsfilename;

	bool bbias = false;
	bool bdarks = false;
	bool bflats = false;

	std::cout << "\tLos archivos fits son: " << std::endl;
	for (list<string>::iterator itr = FitsFileList.begin(); itr != FitsFileList.end(); itr++) {
		std::cout << *itr << std::endl;
	}

	std::cout << std::endl;
	if (Biastxt.size() > 0) {
		biasfilename = *Biastxt.begin();
		std::cout << "El archivo Bias es: " << biasfilename << std::endl;
		bbias = true;
	}
	else {
		std::cout << "El archivo Bias es: - " << std::endl;
		bbias = false;
	}
	if (Darkstxt.size() > 0) {
		darksfilename = *Darkstxt.begin();
		std::cout << "El archivo Darks es: " << darksfilename << std::endl;
		bdarks = true;
	}
	else {
		std::cout << "El archivo Darks es: - " << std::endl;
		bdarks = false;
	}
	if (Flatstxt.size() > 0) {
		flatsfilename = *Flatstxt.begin();
		std::cout << "El archivo Flats es: " << flatsfilename << std::endl;
		bflats = true;
	}
	else {
		std::cout << "El archivo Flats es: - " << std::endl;
		bflats = false;
	}
#pragma endregion

	std::cout << "\n\n" << std::endl;
	std::cout << "\tObtenemos Bias, Darks y Flats Master\n" << std::endl;

#pragma region Conseguir Bias, Darks y Flats Master

	Bias bias;
	Darks darks;
	Flats flats;

	string filess = FITS_FILE_PATH + *FitsFileList.begin();
	long * ns;
	int xx;
	int yy;
	ns = GetFitsAxes(filess);
	xx = ns[0];
	yy = ns[1];

	if (bbias) {
		bias.setNewBias(FITS_FILE_PATH + biasfilename);
		bias.Init();
		bias.ObtainMaster();
		bias.free_data();
	}
	else {
		bias.setNullMaster(xx, yy);
	}

	if (bdarks) {
		darks.setNewDarks(FITS_FILE_PATH + darksfilename);
		darks.Init();
		darks.ObtainMaster(bias.getMaster());
		darks.free_data();
	}
	else {
		darks.setNullMaster(xx, yy);
	}

	if (bflats) {
		flats.setNewFlats(FITS_FILE_PATH + flatsfilename);
		flats.Init();
		flats.ObtainMaster(bias.getMaster(), darks.getMaster());
		flats.free_data();
	}
	else {
		flats.setNullMaster(xx, yy);
	}
#pragma endregion

	std::cout << "\n\n" << std::endl;
	std::cout << "\tComenzamos proceso de lectura de archivos fits\n" << std::endl;

	Science mainfile;
	string currentfile;
	long * axes;
	int xs;
	int ys;
	int frames;
	int xys;
	int total_read_data;

	//Puntero a frame binarizado
	long* binaryframe = NULL;

	std::cout << "Comenzamos con el Archivo " << *FitsFileList.begin() << std::endl;

	//Obtenemos un frame binarizado de primer archivo fits para obtener los puntos de partida para observar
	//las estrellas.
	currentfile = FITS_FILE_PATH + *FitsFileList.begin();

	axes = GetFitsAxes(currentfile);
	xs = axes[0];
	ys = axes[1];
	frames = axes[2];
	
	mainfile.ChangeFile(currentfile);
	mainfile.setAxes(xs, ys, frames);
	mainfile.Init();
	mainfile.CleanData(bias.getMaster(), darks.getMaster(), flats.getMaster());

	binaryframe = preprocess(mainfile.Data(), mainfile.Xs, mainfile.Ys, cosum_frames, max_pct);

	//Imprimimos una ayuda visual del archivo binarizado
	WriteFitsImage("Binary_Frame.fits", 2, mainfile.Xs, mainfile.Ys, binaryframe);

	//Realizamos una verificación del la binarización, si mas del 20% de la imagen es tomada como estrella
	//entonces hay un error con el porcentaje utilizado
	if (!Veryfication(mainfile.Xs, mainfile.Ys, binaryframe)) {
		//Hay un error con el porcentaje utilizado
		std::cout << "\n\nError: el porcentaje de valor maximo utilizado es muy bajo." << std::endl;
		std::cout << "Se sugiere elevar el porcentaje. \n" << std::endl;

		mainfile.free_data();
		bias.free_Master();
		darks.free_Master();
		flats.free_Master();

		return(EXIT_SUCCESS);
	}

	//Despues de obtener el frame binarizado obtenemos coordenadas inciales de las estrellas
	std::vector<Star> stars = StarSearch(binaryframe, mainfile.Xs, mainfile.Ys, radius_pct);

	std::cout << "\nSe encontraron " << stars.size() << " estrellas\n" << std::endl;

	std::cout << "Recortando estrellas ..." << std::endl;
	StarTrace(stars, mainfile.Data(), mainfile.Xs, mainfile.Ys, mainfile.Frames, radius_pct);

	//Una vez que tenemos la informacion de todo el primer archivo, creamos los archivos hdf5
	stringstream ss;
	Star star;
	int i = 0;

	//Escribimos y despues limpiamos la inforamción actual de las estrellas
	std::cout << "Creamos archivos hdf5 ... " << std::endl;

	vector<string> filenames = create_filenames(*FitsFileList.begin(), stars);

	for (std::vector<Star>::iterator itr = stars.begin(); itr != stars.end(); itr++)
	{
		star = *itr;
		CreateHDF5(filenames[i], &star, frames);
		i++;
		itr->StarReset();
	}

	//Comenzamos a sumar los frames que estamos trabajando
	unsigned int current_frames = mainfile.Frames;

	//Comenzamos a iterar los archivos (menos el primero) a traves de los procesos (menos binarizacion
	//y localizacion de estrellas)
	FitsFileList.pop_front();

	for (std::list<string>::iterator itr = FitsFileList.begin(); itr != FitsFileList.end(); itr++)
	{
		currentfile = FITS_FILE_PATH + *itr;

		std::cout << "\nComenzamos con el siguiente archivo: " << *itr << std::endl;

		//Checamos cuantos frames tiene el nuevo archivo
		axes = GetFitsAxes(currentfile);
		xs = axes[0];
		ys = axes[1];
		frames = axes[2];

		mainfile.ChangeFile(currentfile);
		mainfile.setAxes(xs, ys, frames);
		mainfile.Init();
		mainfile.CleanData(bias.getMaster(), darks.getMaster(), flats.getMaster());

		StarTrace(stars, mainfile.Data(), mainfile.Xs, mainfile.Ys, mainfile.Frames, radius_pct);

		//Escribimos y despues limpiamos la inforamción actual de las estrellas
		std::cout << "Escribimos en archivos hdf5 ... " << std::endl;
		i = 0;
		for (std::vector<Star>::iterator itr = stars.begin(); itr != stars.end(); itr++)
		{
			star = *itr;
			AddtoHDF5(filenames[i], &star, frames, current_frames);
			i++;
			itr->StarReset();
		}

		current_frames = current_frames + mainfile.Frames;

		std::cout << std::endl;
	}

	std::cout << "\n\n" << std::endl;
	std::cout << "\tLiberamos memoria\n" << std::endl;

	//Liberamos memoria
	mainfile.free_data();
	bias.free_Master();
	darks.free_Master();
	flats.free_Master();

	//Realizamos fotometría
	string filename;
	i = 0;

	for (std::vector<Star>::iterator itr = stars.begin(); itr != stars.end(); itr++)
	{
		star = *itr;
		filename = VIEW_HDF5_PATH;
		filename = filename + filenames[i];

		Photometry(filename, star, lz);

		ss.str(std::string());
		i++;
	}

	//Fin del programa
	std::cout << std::endl;
	exit(EXIT_SUCCESS);
}

vector<string> create_filenames(string fits_name, vector<Star> stars) {

	stringstream ss;

	vector<string> files;
	string file_prefix;
	string file_suffix;
	string file_name;

	file_prefix = fits_name.substr(0, fits_name.find("."));
	file_prefix.append("_Star_");

	file_suffix = ".hdf5";

	for (int i = 0; i < stars.size(); i++)
	{
		file_name.append(file_prefix);

		ss << stars[i].getCenter().getX() + 1;
		file_name.append(ss.str());
		file_name.append("_");

		ss.str(std::string());

		ss << stars[i].getCenter().getY() + 1;
		file_name.append(ss.str());

		ss.str(std::string());

		file_name.append(file_suffix);
		
		files.push_back(file_name);
		file_name.clear();
	}

	return files;

}

void WriteFitsImage(string filename, int naxis, long X, long Y, long int* data) {
	
	fitsfile* salida;
	int status = 0;
	long int naxes[] = { X, Y };
	long nelements = X * Y;
	long fpixel = 1;

	filename = string("!") + VIEW_DS9_PATH + filename;

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

	if (fits_create_file(&salida, file, &status)) {
		fits_report_error(stderr, status); //Imprime error de status
	}

	if (fits_create_img(salida, LONG_IMG, naxis, naxes, &status)) {
		fits_report_error(stderr, status); //Imprime error de status
	}

	if (fits_write_img(salida, TLONG, fpixel, nelements, &data[0], &status)) {
		fits_report_error(stderr, status); //Imprime error de status
	}

	//Cerramos el archivo
	if (fits_close_file(salida, &status)) {
		fits_report_error(stderr, status); //Imprime error de status
	}

}

bool Veryfication(long X, long Y, long int* data) {

	long int total_pixels = X * Y;
	long int counter = 0;

	for (int i = 0; i < total_pixels; i++) {
		if (data[i] == 1) {
			counter++;
		}
	}

	if (counter > (total_pixels * 0.2)) {
		return false;
	}
	
	return true;
}