#include "stack-c.h"
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <errno.h>
#include "hdf5.h"

//extern void displ __PARAMS((double *a, int ma, int na, int *b, int mb, int nb, char *c, int mc, int nc));

#define MINRHS		2
#define MAXRHS		2
#define MINLHS		1
#define MAXLHS		2


/** Gets HDF5 Dataset - up to 2D array of numbers or strings, numbers always returned as double */
int getDSet(char *fileName, char *name, int *nxPtr, int *nyPtr, void **dataPtr, int *strLen) {
	int file_id;
	int dataspace_id, dataset_id, type_id, class_id, status;
	int rank, i, ret;
	hsize_t *dims = NULL;
	long totalEntries, totalBytes;
	int type_class, type_size;
	void *data = NULL;

	/* Open an existing file. */
	file_id = H5Fopen(fileName, H5F_ACC_RDWR, H5P_DEFAULT);
	if(file_id <= 0){
		sciprint("Unable to open HDF5 file.\n");
		ret = -EIO;
		goto getDSet_done;
	}
	
	/* Open an existing dataset. */
	dataset_id = H5Dopen(file_id, name);
	if(dataset_id <= 0){
		sciprint("Unable to open HDF5 dataset '%s'\n", name);
		goto getDSet_done;
		return -EIO;
	}

	dataspace_id = H5Dget_space(dataset_id);
	if(dataspace_id <= 0){
		sciprint("Unable to get HDF5 dataspace from '%s'\n", name);
		ret = -EIO;	
		goto getDSet_done;
	}

	rank = H5Sget_simple_extent_ndims(dataspace_id);
	if(rank < 1 || rank > 2){
		sciprint("Dataset wasn't 1D or 2D\n");
		ret = -EINVAL;
		goto getDSet_done;
	}

	dims = (hsize_t *)malloc(rank * sizeof(hsize_t));
	if(!dims){
		sciprint("Unable to allocatespace for dims of dataset '%s'\n", name);
		ret = -ENOMEM;
		goto getDSet_done;
	}
	H5Sget_simple_extent_dims(dataspace_id, dims, NULL);

	type_id = H5Dget_type(dataset_id);
	class_id = H5Tget_class(type_id);
	switch(class_id){
	case H5T_INTEGER:
	case H5T_FLOAT:
		*strLen = 0;
		type_id = H5Tcopy(H5T_NATIVE_DOUBLE);
		break;
	case H5T_STRING:
		*strLen = H5Tget_size(type_id);
		/* Keep the existing type  */
		break;
	default:
		sciprint("Dataset has unsupported class '%i', can only handle ints, floats or strings\n", class_id);
	}
	
	totalBytes = H5Tget_size(type_id); /* type_id is now target - the buffer */
	for(i=0;i<rank;i++)
		totalBytes *= (int)dims[i];

	data = malloc(totalBytes);
	if(!data){
		sciprint("Unable to allocate %li bytes to read HDF5 dataset '%s'\n", totalBytes, name);
		ret = -ENOMEM;	
		goto getDSet_done;
	}


	status = H5Dread(dataset_id, type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data);	
	if(status){
		sciprint("Error reading data from HDF5 dataset '%s'", name);
		ret = -EIO;
		goto getDSet_done;
	}

	ret = 0;
	
getDSet_done:
	if(dataset_id > 0){
		/* Close the dataset. */
		status = H5Dclose(dataset_id);
		if(status) sciprint("Error %i closing HDF5 dataset\n", status);
	}

	if(file_id > 0){
		/* Close the file. */	
		status = H5Fclose(file_id);
		if(status) fprintf(stderr, "Error %i closing HDF5 file\n", status);
	}

	if(ret){
		if(data) free(data);
		if(dims) free(dims);
		if(nxPtr) *nxPtr = 0;
		if(nyPtr) *nyPtr = 0;
		if(dataPtr) *dataPtr = NULL;
	}else{
		if(dims){
			if(nxPtr) *nxPtr = dims[0];
			if(nyPtr) *nyPtr = (rank > 1) ? dims[1] : 0;
			free(dims);
		}
		if(dataPtr) *dataPtr = data; else free(data);
	}
	return ret;
}

/* //Test proc for scilab interface
int getDSet(char *fileName, char *dataSet, int *nx, int *ny, double **d) {
	int i, len;
	*nx = 3;
	*ny = 2;
	len = (*nx) * (*ny);
	*d = (double *)malloc(len * sizeof(double));
	if(!*d){ sciprint("malloc failed\n"); return -ENOMEM; }
	sciprint("Read dataset '%s' from '%s'\n", fileName, dataSet);
	for(i=0;i<len;i++)
		(*d)[i] = (double)i / 100.0;
	
	return 0;
}
*/

/* LHS: Data matrix
   RHS: 
	String fileName
	String datasetName
*/
int getH5DSet(char *fname) {
	static char *hdf5FName, *dataset;
	static int l1, m1, n1, l2, m2, n2, m4, n4, l4, nx, ny, l3, ret;
	static void *d;
	static int strLen = 0;
	static int rank = 0;
	
	CheckRhs(MINRHS, MAXRHS);
	CheckLhs(MINLHS, MAXLHS);

	GetRhsVar(1, "c", &m1, &n1, &l1);
	GetRhsVar(2, "c", &m2, &n2, &l2);

	/* Call display function
	stk(l1)->X (double), istk(l2)->Y (int), cstk(l3)->Z  (char)    */
	//displ(stk(l1), m1, n1, istk(l2), m2, n2, cstk(l3), m3, n3);
	ret = getDSet(cstk(l1), cstk(l2), &nx, &ny, &d, &strLen);
	if(ret){
		sciprint("Error %i from getDSet()\n", ret);
		LhsVar(1) = 0;
		return 0;
	}

	if(ny == 0){
		rank = 1;
		ny = 1;
	} else
		rank = 2;
	
	if(strLen > 0){
		char *strBuff = d;
		char **strs;
		int i;
		/* Need to compile a char ** */
		strs = (char **)malloc(nx * ny * sizeof(char *));
		if(!strs){
			sciprint("Error: Couldn't alloc space for matrix of string pointers\n");
			return 0;
		}
		for(i=0; i < (nx * ny); i++)
			strs[i] = strBuff + (i * strLen );			
		
		CreateVarFromPtr(3, "S", &ny, &nx, strs);
//		memcpy(stk(l3), d, nx * ny * strLen);
	}else{
		CreateVar(3, "d", &ny, &nx, &l3);
		memcpy(stk(l3), d, nx * ny * sizeof(double));
	}
	free(d);

	//Return the rank so they can tell diff between (n x 1) 2D array and a n-long 1D array
	m4 = 1; n4 = 1;
	CreateVar(4, "i", &m4, &n4, &l4);
	*((int *)istk(l4)) = rank;
	
	LhsVar(1) = 3;
	LhsVar(2) = 4;

	return 0;
}

