#pragma once
#include <hdf5.h>
#include <string>

#include "Star.h"
#include "taos.h"

using namespace std;

void CreateHDF5(string filename, Star* star, int frames);
void AddtoHDF5(string filename, Star* star, int frames, unsigned int current_frames);
void hdf5_save_vector2(hid_t group, string name, int* data, long int frames);
void hdf5_save_vector2(hid_t group, string name, double* data, long int frames);
void hdf5_save_image(hid_t group, string name, double* data, long int tam, long int frames);
void hdf5_add_vector2(hid_t group, string name, int frames_offset, int data_tam, int* data);
void hdf5_add_vector2(hid_t group, string name, int frames_offset, int data_tam, double* data);
void hdf5_add_image(hid_t group, string name, long int frames_offset, long int frames, int data_tam, double* data);


void CreateHDF5(string filename, Star* star, int frames) {

	herr_t status;
	
	//Creamos el archivo que contendra la información de la estrella
	hid_t file_id;
	filename = VIEW_HDF5_PATH + filename;
	file_id = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);

	//Creamos todos los grupos que tendra el archivo
	hid_t grp1_id = H5Gcreate2(file_id, CURVE_CENTER, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
	hid_t grp2_id = H5Gcreate2(file_id, CURVE_DIFF_GROUP, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
	hid_t grp3_id = H5Gcreate2(file_id, DATA, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
	hid_t grp4_id = H5Gcreate2(file_id, STAR_CENTER, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
	
	int tam = star->getSquareSide();

	//Escribimos la información de la estrella
	hdf5_save_image(grp4_id, STAR_DATA, star->getData(), tam, frames);

	//Escribimos la infomación del centro de la estrella
	hdf5_save_vector2(grp3_id, POS_X, star->point_x_in_time.data(), frames);
	hdf5_save_vector2(grp3_id, POS_Y, star->point_y_in_time.data(), frames);
	
	//Escribimos el movimiento relativo y absoluto de la estrella
	hdf5_save_vector2(grp3_id, ABS_MOV, star->absolute_movement.data(), frames);
	hdf5_save_vector2(grp3_id, REL_MOV, star->relative_movement.data(), frames);
	
	//Cerramos grupos
	status = H5Gclose(grp1_id);
	status = H5Gclose(grp2_id);
	status = H5Gclose(grp3_id);
	status = H5Gclose(grp4_id);

	//Cerramos el archivo
	status = H5Fclose(file_id);
}

void AddtoHDF5(string filename, Star* star, int frames, unsigned int current_frames) {

	herr_t status;

	//Abrimos archivo
	hid_t rfile_id;

	filename = VIEW_HDF5_PATH + filename;
	rfile_id = H5Fopen(filename.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);

	//Abrimos los grupos que usaremos
	hid_t rgroup3_id = H5Gopen2(rfile_id, DATA, H5P_DEFAULT);
	hid_t rgroup4_id = H5Gopen2(rfile_id, STAR_CENTER, H5P_DEFAULT);

	//Agregamos información del centro de la estrella
	hdf5_add_image(rgroup4_id, STAR_DATA, current_frames, frames, star->getSquareSide(), star->getData());

	//Agregamos información del movimiento en X y Y
	hdf5_add_vector2(rgroup3_id, POS_X, current_frames, star->point_x_in_time.size(), star->point_x_in_time.data());
	hdf5_add_vector2(rgroup3_id, POS_Y, current_frames, star->point_y_in_time.size(), star->point_y_in_time.data());

	//Agregamos información del movimiento absoluto y relativo
	hdf5_add_vector2(rgroup3_id, ABS_MOV, current_frames, star->absolute_movement.size(), star->absolute_movement.data());
	hdf5_add_vector2(rgroup3_id, REL_MOV, current_frames, star->relative_movement.size(), star->relative_movement.data());

	//Cerramos grupos
	status = H5Gclose(rgroup3_id);
	status = H5Gclose(rgroup4_id);

	//Cerramos archivo
	status = H5Fclose(rfile_id);

}

void hdf5_save_vector2(hid_t group, string name, int* data, long int frames) {

	herr_t status;

	//Habilitamos la propiedad de chunking para el dataset
	hid_t dcpl_id;
	hsize_t chunk_dims[2] = { 1, 1 };
	dcpl_id = H5Pcreate(H5P_DATASET_CREATE);
	status = H5Pset_chunk(dcpl_id, 2, chunk_dims);

	//Creamos dataspace
	hid_t dataspace_id;
	hsize_t maxdims[] = { H5S_UNLIMITED, 1 };
	hsize_t dims[2];

	dims[1] = 1;
	dims[0] = frames;
	dataspace_id = H5Screate_simple(2, dims, maxdims);

	//Creamos el dataset
	hid_t dataset_id;

	dataset_id = H5Dcreate2(group, name.c_str(), H5T_NATIVE_INT, dataspace_id,
		H5P_DEFAULT, dcpl_id, H5P_DEFAULT);

	//Escribimos la informacion
	status = H5Dwrite(dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data);

	//Cerramos dataset, space, chunks y el grupo
	status = H5Dclose(dataset_id);
	status = H5Sclose(dataspace_id);
	status = H5Pclose(dcpl_id);

}

void hdf5_save_vector2(hid_t group, string name, double* data, long int frames) {

	herr_t status;

	//Habilitamos la propiedad de chunking para el dataset
	hid_t dcpl_id;
	hsize_t chunk_dims[2] = { 1, 1 };
	dcpl_id = H5Pcreate(H5P_DATASET_CREATE);
	status = H5Pset_chunk(dcpl_id, 2, chunk_dims);

	//Creamos dataspace
	hid_t dataspace_id;
	hsize_t maxdims[] = { H5S_UNLIMITED, 1 };
	hsize_t dims[2];

	dims[1] = 1;
	dims[0] = frames;
	dataspace_id = H5Screate_simple(2, dims, maxdims);

	//Creamos el dataset
	hid_t dataset_id;

	dataset_id = H5Dcreate2(group, name.c_str(), H5T_IEEE_F64LE, dataspace_id,
		H5P_DEFAULT, dcpl_id, H5P_DEFAULT);

	//Escribimos la informacion
	status = H5Dwrite(dataset_id, H5T_IEEE_F64LE, H5S_ALL, H5S_ALL, H5P_DEFAULT, data);

	//Cerramos dataset, space, chunks y el grupo
	status = H5Dclose(dataset_id);
	status = H5Sclose(dataspace_id);
	status = H5Pclose(dcpl_id);

}

void hdf5_save_image(hid_t group, string name, double* data, long int tam, long int frames) {

	herr_t status;

	//Habilitamos la propiedad de chunking para el dataset
	hid_t dcpl_id;
	hsize_t chunk_dims[3] = { 1, tam, tam };
	dcpl_id = H5Pcreate(H5P_DATASET_CREATE);
	status = H5Pset_chunk(dcpl_id, 3, chunk_dims);

	//Creamos dataspace
	hid_t dataspace_id;
	hsize_t maxdims[] = { H5S_UNLIMITED, tam, tam };
	hsize_t dims[3];

	dims[2] = tam;
	dims[1] = tam;
	dims[0] = frames;
	dataspace_id = H5Screate_simple(3, dims, maxdims);

	//Creamos el dataset
	hid_t dataset_id;
	dataset_id = H5Dcreate2(group, name.c_str(), H5T_IEEE_F64LE, dataspace_id,
		H5P_DEFAULT, dcpl_id, H5P_DEFAULT);

	//Escribimos la informacion
	status = H5Dwrite(dataset_id, H5T_IEEE_F64LE, H5S_ALL, H5S_ALL, H5P_DEFAULT, data);

	//Cerramos dataset, space, chunks y el grupo
	status = H5Dclose(dataset_id);
	status = H5Sclose(dataspace_id);
	status = H5Pclose(dcpl_id);

}

void hdf5_add_vector2(hid_t group, string name, int frames_offset, int data_tam, int* data) {

	herr_t status;

	//Abrimos dataset
	hid_t dataset_id;
	dataset_id = H5Dopen2(group, name.c_str(), H5P_DEFAULT);

	//Extendemos el dataset
	int tam = data_tam;
	hsize_t dimext[2];		//Tamaño de la información a agregar
	dimext[0] = tam;
	dimext[1] = 1;

	hsize_t size[2];	//Nuevo tamaño que tendria el dataset
	size[1] = 1;		//Este eje permanece constante
	size[0] = frames_offset + data_tam;		//Se suman los datos actuales mas los datos a agregar
	status = H5Dset_extent(dataset_id, size);

	//Abrimos dataspace
	hid_t dspace_id;
	hsize_t offset[2];

	offset[1] = 0;
	offset[0] = frames_offset;	//Offset de frames
	dspace_id = H5Dget_space(dataset_id);
	status = H5Sselect_hyperslab(dspace_id, H5S_SELECT_SET, offset, NULL, dimext, NULL);

	hid_t memspace;
	memspace = H5Screate_simple(2, dimext, NULL);

	status = H5Dwrite(dataset_id, H5T_NATIVE_INT, memspace, dspace_id, H5P_DEFAULT, data);

	status = H5Dclose(dataset_id);
	status = H5Sclose(dspace_id);

}

void hdf5_add_vector2(hid_t group, string name, int frames_offset, int data_tam, double* data) {

	herr_t status;

	//Abrimos dataset
	hid_t dataset_id;
	dataset_id = H5Dopen2(group, name.c_str(), H5P_DEFAULT);

	//Extendemos el dataset
	int tam = data_tam;
	hsize_t dimext[2];		//Tamaño de la información a agregar
	dimext[0] = tam;
	dimext[1] = 1;

	hsize_t size[2];	//Nuevo tamaño que tendria el dataset
	size[1] = 1;		//Este eje permanece constante
	size[0] = frames_offset + data_tam;		//Se suman los datos actuales mas los datos a agregar
	status = H5Dset_extent(dataset_id, size);

	//Abrimos dataspace
	hid_t dspace_id;
	hsize_t offset[2];

	offset[1] = 0;
	offset[0] = frames_offset;	//Offset de frames
	dspace_id = H5Dget_space(dataset_id);
	status = H5Sselect_hyperslab(dspace_id, H5S_SELECT_SET, offset, NULL, dimext, NULL);

	hid_t memspace;
	memspace = H5Screate_simple(2, dimext, NULL);

	status = H5Dwrite(dataset_id, H5T_IEEE_F64LE, memspace, dspace_id, H5P_DEFAULT, data);

	status = H5Dclose(dataset_id);
	status = H5Sclose(dspace_id);

}

void hdf5_add_image(hid_t group, string name, long int frames_offset, long int frames, int data_tam, double* data) {

	herr_t status;

	//Abrimos dataset
	hid_t dataset_id;
	dataset_id = H5Dopen2(group, name.c_str(), H5P_DEFAULT);

	//Extendemos el dataset
	int tam = data_tam;
	hsize_t dimext[3];		//Tamaño de la información a agregar
	dimext[0] = frames;		//frames
	dimext[1] = tam;		//Xs
	dimext[2] = tam;		//Ys

	hsize_t size[3];	//Nuevo tamaño que tendria el dataset
	size[2] = tam;		//Este eje permanece constante
	size[1] = tam;		//Este eje permanece constante
	size[0] = frames_offset + frames;		//Se suman sus frames actuales mas los frames a agregar
	status = H5Dset_extent(dataset_id, size);

	//Abrimos dataspace
	hid_t dspace_id;
	hsize_t offset[3];

	offset[2] = 0;
	offset[1] = 0;
	offset[0] = frames_offset;	//Offset de frames
	dspace_id = H5Dget_space(dataset_id);
	status = H5Sselect_hyperslab(dspace_id, H5S_SELECT_SET, offset, NULL, dimext, NULL);

	hid_t memspace;
	memspace = H5Screate_simple(3, dimext, NULL);

	status = H5Dwrite(dataset_id, H5T_IEEE_F64LE, memspace, dspace_id, H5P_DEFAULT, data);

	status = H5Dclose(dataset_id);
	status = H5Sclose(dspace_id);

}
