/* $Revision: 1.1 $ */
#include "hdf5.h"
#include "mex.h"
#include "hdf5utils.h"

static bool mexAtExitIsSet = false;

/* binky - Take this out */
#define MAX_FILENAME_LEN   500   /* Max length of an HDF5 filename that we allow */

/*
 * Forward declarations of functions within this file
 */
group_info get_object_info(hid_t, char *, char *);
mxArray *  datatype_to_description(hid_t);
mxArray *  create_group_structure(hid_t, char *, char *, group_info, bool);
mxArray *  create_dataset_structure(hid_t, const char *, const char *, ginfo_ptr, bool);
mxArray *  create_named_datatype_structure(hid_t, const char *, const char *, ginfo_ptr);
mxArray *  create_datatype_structure(hid_t);
mxArray *  create_attribute_structure(hid_t, const char *, const char *, ginfo_ptr, bool);
mxArray *  create_link_structure(hid_t, const char *, ginfo_ptr, bool, const char*);
herr_t     get_num_members(hid_t, const char *, ginfo_ptr);
void       free_heap_memory(ginfo_ptr);

/*
 * This is an entry in the table which has room to store object numbers
 * and the corresponding object name
 */
struct entry {
    unsigned long objno[2];
    char *objname;
};

int num_entries = 0;

/* 
 * This is table keeps track of the objects with more than 1 hard
 * link pointing to it.  It stores the name of the object in the table.
 */

/* TBD- turn this into a linked list */
struct entry table[1000];  /* no more than 1000 objects allowed */

/* This function looks up an object, and returns the index into
 * the table if it's found.  If it isn't found, it returns -1.
 */

int find_entry( unsigned long objno[2]) {
int i;
        for (i = 0; i < num_entries; i++) {
                if ((objno[0] == table[i].objno[0]) && 
                        (objno[1] == table[i].objno[1])) {
                        return i;
                }
        }
        return -1;
}

/* This puts an object into the table if it hasn't been found
 */

int insert_entry( unsigned long objno[2], char *objname) {
    int rv;
    if ((rv = find_entry(objno)) > 0) {
	return(rv);
    }
    else {
	if (num_entries >= 1000) {
	    mexPrintf("Exceeded number of hardlinks available");
	}
        table[num_entries].objno[0] = objno[0];
        table[num_entries].objno[1] = objno[1];
	table[num_entries].objname = mxMalloc(strlen(objname) * sizeof(char) + 1);
	strcpy(table[num_entries].objname,objname);
        num_entries++;
        return(num_entries);
    }
}


/*
 * This is the main entry point of the MEX-file
 */
void mexFunction(int nlhs, mxArray *plhs[], 
                 int nrhs, const mxArray *prhs[] )

{
    char       *filename;             /* name of HDF5 file */
    hid_t      fid;                   /* fid for opening the HDF5 file */
    hid_t      plist;                 /* file creation property list */
    bool       read_attribute_values; /* whether or not to read attribute values */
    hsize_t    block_size;            /* block_size (offset) before data starts */
    herr_t     err;                   /* error ID */
    mxArray    *group_hierarchy;      /* group structure for root group */
    int        num_group_fields;      /* number of fields for the group structure */
    group_info ginfo;                 /* group info struct for root group */
    unsigned majnum;
    unsigned minnum;
    unsigned relnum;

    num_group_fields = GET_NUMBER_FIELDNAMES(group_fields);
    
    if (! mexAtExitIsSet)
    {
        H5dont_atexit();
	InitializeLists();
        mexAtExit(h5MexAtExit);
        mexAtExitIsSet = true;
    }

#if SUPPRESS_BACKTRACE
    /*
     * Turn off HDF5's automatic backtracing
     */
    H5Eset_auto(NULL, NULL);
#endif

    /*
     * Get filename and open it.
     */

    filename = (char *) mxMalloc((MAX_FILENAME_LEN + 1) * sizeof(char));

    if ((!mxIsChar(prhs[0])) || 
        (mxGetString(prhs[0], filename, (MAX_FILENAME_LEN + 1)))) {
        errHandler("Invalid file name.");
    }

    fid = open_file(filename, H5F_ACC_RDONLY, H5P_DEFAULT);

    /*
     * Should we read in the attribute values or just provide info?
     */
    if (nrhs > 1)
    {
        read_attribute_values = *(mxGetLogicals(prhs[1]));
    }else
    {
        read_attribute_values = true;
    }

    /*
     * Get the offset, also known as the "user-block" from a file
     * creation property list.
     */
    plist = H5Fget_create_plist(fid);
    err = H5Pget_userblock(plist, &block_size);
    
    if (err < 0)
    {
        errHandler("Error getting user-block size");
    }

    plhs[0] = mxCreateDoubleScalar((int) block_size);

    /*
     * Get the version of the library that we are working with
     */
    H5get_libversion(&majnum, &minnum, &relnum);
    plhs[2] = mxCreateDoubleScalar(majnum);
    plhs[3] = mxCreateDoubleScalar(minnum);
    plhs[4] = mxCreateDoubleScalar(relnum);

    /*
     * Get Group Hierarchy which is a group structure
     */
    plhs[1] = mxCreateStructMatrix(1, 1, num_group_fields, group_fields);

    ginfo = get_object_info(fid, filename, "/");

    group_hierarchy = create_group_structure(fid, filename, "/", ginfo, read_attribute_values);
    mxSetField(plhs[1], 0, "Filename", mxCreateString(filename));
    mxSetField(plhs[1], 0, "Name", mxCreateString("/"));
    if (group_hierarchy != NULL)
    {
        mxSetField(plhs[1], 0, "Groups", group_hierarchy);
    }
    /*
     * Create the dataset structure.
     */
    if (ginfo.num_datasets > 0)
    {
        mxArray * dataset_structure;
        
        dataset_structure = create_dataset_structure(fid, "/", filename, &ginfo, read_attribute_values);
        mxSetField(plhs[1], 0, "Datasets", dataset_structure);
    }

    /*
     * Create the attributes structure.
     */
    if (ginfo.num_attributes > 0)
    {
        mxArray * attribute_structure;
        
        attribute_structure = create_attribute_structure(fid, filename, "/", &ginfo, read_attribute_values);
        mxSetField(plhs[1], 0, "Attributes", attribute_structure);
    }

    /*
     * Create the datatypes structure.
     */
    if (ginfo.num_datatypes > 0)
    {
        mxArray * datatype_structure;
        
        datatype_structure = create_named_datatype_structure(fid, filename, "/", &ginfo);
        mxSetField(plhs[1], 0, "Datatypes", datatype_structure);
    }

    /*
     * Create the attributes structure.
     */
    if (ginfo.num_links > 0)
    {
        mxArray * link_structure;
        
        link_structure = create_link_structure(fid, "/", &ginfo, false, NULL);
        mxSetField(plhs[1], 0, "Links", link_structure);
    }

    /*
     * Close/release resources.
     */

    free_heap_memory(&ginfo);

    close_file(fid);
    mxFree(filename);
}

/*
 * Operator for H5Giterate.  Gets the number of member objects
 * within a certain group.
 */
herr_t get_num_members(hid_t loc_id, const char *member_name, ginfo_ptr ginfo)
{
    
    H5G_stat_t statbuf;

    H5Gget_objinfo(loc_id, member_name, false, &statbuf);
    switch (statbuf.type) {
      case H5G_GROUP:
        ginfo->num_groups++;
	break;
      case H5G_DATASET:
        ginfo->num_datasets++;
        break;
      case H5G_TYPE:
	/*
	 * Why doesn't this pick up un-named committed types?
	 */
        ginfo->num_datatypes++;
        break;
      case H5G_LINK:
        ginfo->num_links++;
        break;
      default:
        errHandler("Unable to identify object");
    }

    return 0;
}

/*
 * Frees heap memory that was allocated for group_info_structure
 */
void free_heap_memory(ginfo_ptr ginfo)
{
    int i;

    for (i = 0; i < ginfo->num_groups; i++)
    {
        mxFree(ginfo->group_names[i]);
	if (ginfo->objno != NULL)
	{
	    mxFree(ginfo->objno[i]);
	}
    }
    
    if (ginfo->objno != NULL)
    {
	mxFree(ginfo->objno);
    }

    /* Check ptr for NULL before trying to free */
    if (ginfo->group_names != NULL)
    {
        mxFree(ginfo->group_names);
    }

    for (i = 0; i < ginfo->num_datatypes; i++)
    {
        mxFree(ginfo->named_datatype_names[i]);
        /*
         * Need to free the class names, too.
         */
    }

    if (ginfo->named_datatype_names != NULL)
    {
        mxFree(ginfo->named_datatype_names);
    }

    for (i = 0; i < ginfo->num_datasets; i++)
    {
        mxFree(ginfo->dataset_names[i]);
    }
    /* Check ptr for NULL before trying to free */
    if (ginfo->dataset_names != NULL)
    {
        mxFree(ginfo->dataset_names);
    }

    for (i = 0; i < ginfo->num_links; i++)
    {
        mxFree(ginfo->link_names[i]);
    }
    /* Check ptr for NULL before trying to free */
    if (ginfo->link_names != NULL)
    {
        mxFree(ginfo->link_names);
    }
    
    for (i = 0; i < ginfo->num_attributes; i++)
    {
        mxFree(ginfo->attribute_names[i]);
    }

    if (ginfo->attribute_names != NULL)
    {
        mxFree(ginfo->attribute_names);
    }

}

/*
 * Goes through and creates the hierarchy of information regarding all of the
 * objects in the given group name.
 */
group_info get_object_info(hid_t fid, char *filename, char *group_name)
{
    int status;     /* status of H5 API function completing */

    int num_datatype_fields; /* number of fields for datatypes */
    int num_link_fields; /* number of fields for links */
    int num_attribute_fields;
    hid_t group_id;  /* ID of the group */
    struct group_info ginfo = {0,0,0,0,0,0,0,0,0,0,NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};

    num_datatype_fields = GET_NUMBER_FIELDNAMES(datatype_fields);
    num_link_fields = GET_NUMBER_FIELDNAMES(link_fields);
    num_attribute_fields = GET_NUMBER_FIELDNAMES(attribute_fields);

    /*
     * Get all object information within the group.
     */
    status = H5Giterate(fid, group_name, NULL, get_num_members, &ginfo);


    /*
     * Can attributes be hard-linked?  No.  Softlinks cannot be.
     */

    /*
     * Get all attributes attached to the group
     */
    group_id = open_group(fid, group_name);

    ginfo.num_attributes = H5Aget_num_attrs(group_id);

    ginfo.group_names = (char **) mxMalloc(ginfo.num_groups * sizeof(char *));
    ginfo.named_datatype_names = (char **) mxMalloc(ginfo.num_datatypes * sizeof(char *));
    ginfo.dataset_names = (char **) mxMalloc(ginfo.num_datasets * sizeof(char *));
    ginfo.link_names = (char **) mxMalloc(ginfo.num_links * sizeof(char *));
    ginfo.attribute_names = (char **) mxMalloc(ginfo.num_attributes * sizeof(char *));
    ginfo.nlink = (unsigned *) mxMalloc(ginfo.num_groups * sizeof(unsigned));
    ginfo.objno = (unsigned long **) mxMalloc(ginfo.num_groups * sizeof(unsigned long *));
    /* Needs some work if there are multiple named datatypes there */
    /* Don't you want this to be the number of datatypes that are there? */
    ginfo.datatype_class = (int *) mxMalloc(ginfo.num_datasets * sizeof(int));

    if (ginfo.num_attributes > 0)
    {
        status = H5Aiterate(group_id, NULL, populate_attributes, &ginfo);
    }

    status = H5Giterate(fid, group_name, NULL, populate_group_info, &ginfo);

    close_group(group_id);

    return ginfo;
}

/*
 * Re-name this and only call it if there are no named datatypes.
 */
mxArray * create_datatype_structure(hid_t datatype_id)
{
    mxArray *datatype_structure;
    mxArray *elements;
    char *member_name;
    hid_t member_type;
    int elem_dims[2];
    int num_datatype_fields = GET_NUMBER_FIELDNAMES(datatype_fields);
    int num_members;
    int i;

    datatype_structure = mxCreateStructMatrix(1, 1,
                                             num_datatype_fields,
                                             datatype_fields);

    /* 
     * If the name exists for the datatype, then fill the structure field
     
    if (H5Tcommitted(datatype_id) > 0)
    {
	H5G_stat_t statbuf;

	H5Gget_objinfo(datatype_id, ".", TRUE, &statbuf);
	
    }
    */

    switch (H5Tget_class(datatype_id)) {
    case H5T_INTEGER:
	if (H5Tequal(datatype_id, H5T_STD_I8BE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_I8BE"));
	} else if (H5Tequal(datatype_id, H5T_STD_I8LE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_I8LE"));
	} else if (H5Tequal(datatype_id, H5T_STD_I16BE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_I16BE"));
	} else if (H5Tequal(datatype_id, H5T_STD_I16LE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_I16LE"));
	} else if (H5Tequal(datatype_id, H5T_STD_I32BE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_I32BE"));
	} else if (H5Tequal(datatype_id, H5T_STD_I32LE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_I32LE"));
	} else if (H5Tequal(datatype_id, H5T_STD_I64BE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_I64BE"));
	} else if (H5Tequal(datatype_id, H5T_STD_I64LE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_I64LE"));
	} else if (H5Tequal(datatype_id, H5T_STD_U8BE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_U8BE"));
	} else if (H5Tequal(datatype_id, H5T_STD_U8LE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_U8LE"));
	} else if (H5Tequal(datatype_id, H5T_STD_U16BE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_U16BE"));
	} else if (H5Tequal(datatype_id, H5T_STD_U16LE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_U16LE"));
	} else if (H5Tequal(datatype_id, H5T_STD_U32BE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_U32BE"));
	} else if (H5Tequal(datatype_id, H5T_STD_U32LE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_U32LE"));
	} else if (H5Tequal(datatype_id, H5T_STD_U64BE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_U64BE"));
	} else if (H5Tequal(datatype_id, H5T_STD_U64LE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_U64LE"));
	} else if (H5Tequal(datatype_id, H5T_NATIVE_SCHAR)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_NATIVE_SCHAR"));
	} else if (H5Tequal(datatype_id, H5T_NATIVE_UCHAR)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_NATIVE_UCHAR"));
	} else if (H5Tequal(datatype_id, H5T_NATIVE_SHORT)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_NATIVE_SHORT"));
	} else if (H5Tequal(datatype_id, H5T_NATIVE_USHORT)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_NATIVE_USHORT"));
	} else if (H5Tequal(datatype_id, H5T_NATIVE_INT)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_NATIVE_INT"));
	} else if (H5Tequal(datatype_id, H5T_NATIVE_UINT)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_NATIVE_UINT"));
	} else if (H5Tequal(datatype_id, H5T_NATIVE_LONG)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_NATIVE_LONG"));
	} else if (H5Tequal(datatype_id, H5T_NATIVE_ULONG)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_NATIVE_ULONG"));
	} else if (H5Tequal(datatype_id, H5T_NATIVE_LLONG)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_NATIVE_LLONG"));
	} else if (H5Tequal(datatype_id, H5T_NATIVE_ULLONG)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_NATIVE_ULLONG"));
	} else {
	    errHandler("Encountered unknown integer type");
	}
	break;

    case H5T_FLOAT:
	if (H5Tequal(datatype_id, H5T_IEEE_F32BE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_IEEE_F32BE"));
	} else if (H5Tequal(datatype_id, H5T_IEEE_F32LE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_IEEE_F32LE"));
	} else if (H5Tequal(datatype_id, H5T_IEEE_F64BE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_IEEE_F64BE"));
	} else if (H5Tequal(datatype_id, H5T_IEEE_F64LE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_IEEE_F64LE"));
	} else if (H5Tequal(datatype_id, H5T_NATIVE_FLOAT)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_NATIVE_FLOAT"));
	} else if (H5Tequal(datatype_id, H5T_NATIVE_DOUBLE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_NATIVE_DOUBLE"));
	} else if (H5Tequal(datatype_id, H5T_NATIVE_LDOUBLE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_NATIVE_LDOUBLE"));
	} else {
	    errHandler("Encountered unknown float type");
	}
	break;

    case H5T_TIME:
	
	break;

    case H5T_STRING:
	mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STRING"));
	break;

    case H5T_BITFIELD:
	if (H5Tequal(datatype_id, H5T_STD_B8BE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_B8BE"));
	} else if (H5Tequal(datatype_id, H5T_STD_B8LE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_B8LE"));
	} else if (H5Tequal(datatype_id, H5T_STD_B16BE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_B16BE"));
	} else if (H5Tequal(datatype_id, H5T_STD_B16LE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_B16LE"));
	} else if (H5Tequal(datatype_id, H5T_STD_B32BE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_B32BE"));
	} else if (H5Tequal(datatype_id, H5T_STD_B32LE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_B32LE"));
	} else if (H5Tequal(datatype_id, H5T_STD_B64BE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_B64BE"));
	} else if (H5Tequal(datatype_id, H5T_STD_B64LE)) {
	    mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_STD_B64LE"));
	} else {
	    errHandler("Encountered unknown bitfield datatype");
	}
	break;

    case H5T_OPAQUE:
	mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_OPAQUE"));
	mxSetField(datatype_structure, 0, "Name", mxCreateString(H5Tget_tag(datatype_id)));
	break;
    

    case H5T_COMPOUND:

	mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_COMPOUND"));
	/* Add code to do something else if it's a committed type ?
	*/
	num_members = H5Tget_nmembers(datatype_id);
	elem_dims[0] = num_members;
	elem_dims[1] = 2; 

	elements = mxCreateCellArray(2, elem_dims);

	for (i = 0; i < num_members; i++)
	{
	    /*
	     * Or we can print out the names if that would be more helpful
	     */

	    member_name = H5Tget_member_name(datatype_id, i);
	    member_type = H5Tget_member_type(datatype_id, i);

	    mxSetCell(elements, i, mxCreateString(member_name));
	    mxSetCell(elements, i + num_members, datatype_to_description(member_type));
	    close_datatype(member_type);

	}
	mxSetField(datatype_structure, 0, "Elements", elements);
 
	break;

    case H5T_REFERENCE:
	mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_REFERENCE"));
	break;

    case H5T_ENUM:
    	mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_ENUM"));

	/*
	 * Set the Elements/Members field 
	 */
	break;

    case H5T_VLEN:
	mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_VLEN"));
	/* Print the base type somehow.  Nothing more here.
	*/

	break;

    case H5T_ARRAY:
	/* Name should be the base type, maybe? */

	mxSetField(datatype_structure, 0, "Class", mxCreateString("H5T_ARRAY"));
	break;

    default:
	errHandler("Encountered unknown datatype");
	break;
    }
    /*
     * Set the name based on what was recorded
     */
/*
    mxSetField(datatype_structure, 0, "Name", mxCreateString(datatype_name));
*/
    return datatype_structure;
}

/*
 * Returns the dataset structure requested by the info function
 */
mxArray * create_dataset_structure(hid_t fid, const char *group_name, const char *filename, ginfo_ptr ginfo, bool read_attribute_values)
{
    mxArray *dataset_structure;
    int num_dataset_fields; /* number of fields for datasets */
    int i;
    hid_t loc_id;  /* Identifier to the group that the dataset is in */
    bool isimage = false;

    num_dataset_fields = GET_NUMBER_FIELDNAMES(dataset_fields);
    dataset_structure = mxCreateStructMatrix(1, ginfo->num_datasets,
                                             num_dataset_fields,
                                             dataset_fields);
    for (i = 0; i < ginfo->num_datasets; i++)
    {
        hid_t dset;   /* Identifier to dataset */
        hid_t dspace; /* Identifier to dataspace for dataset */
	hid_t dtype;  /* Datatype of dataset */
        hid_t plist;  /* Identifier for creation property list */
        H5G_stat_t statbuf; 
	mxArray *attribute_structure;
	int status;
	void *fvalue;
	mxClassID mltype;
	mxArray *fill_value;
	char *fullname_dataset;
	int j;
        struct group_info attribute_info = {0,0,0,0,0,0,0,0,0,0,NULL, NULL, NULL, NULL, NULL, NULL};

        /*
         * If we have run into a hard link, need to return a link structure
         * AND put it into the link field.  Perhaps we should just
         * automatically dereference any hard link and let that be that.
         */
        loc_id = open_group(fid, group_name);

        /*
         * Populate the dataset structure (array)
         */
        mxSetField(dataset_structure, i, "Filename", mxCreateString(filename));
	/* Why is this strlen being wacky?? */
	/* Add 1 for NULL and 1 for the / */
	if (strcmp(group_name, "/") != 0)
	{
	    fullname_dataset = mxMalloc((strlen(group_name) + strlen(ginfo->dataset_names[i]) + 1 + 1) * sizeof(char));
	    strcpy(fullname_dataset, group_name); 
	    strcat(fullname_dataset,"/");
	    strcat(fullname_dataset, ginfo->dataset_names[i]);
	}else
	{
	    fullname_dataset = mxMalloc((strlen(group_name) + strlen(ginfo->dataset_names[i]) + 1) * sizeof(char));
	    strcpy(fullname_dataset, group_name);
	    strcat(fullname_dataset, ginfo->dataset_names[i]);
	}

	mxSetField(dataset_structure, i, "Name", mxCreateString(fullname_dataset));
        
        /* Rank */
        dset = open_dataset(loc_id, ginfo->dataset_names[i]);
        dspace = H5Dget_space(dset);

        mxSetField(dataset_structure, i, "Rank", mxCreateDoubleScalar(get_rank(dspace)));

        /* Datatype - you want to do an is_committed type thing
	*/

	mxSetField(dataset_structure, i, "Datatype", 
		create_datatype_structure(ginfo->datatype_class[i]));


        /* Dims */
        mxSetField(dataset_structure, i, "Dims", get_dims(dspace));
        /* MaxDims */
        mxSetField(dataset_structure, i, "MaxDims", get_max_dims(dspace));
        
        /* Layout */
        plist = H5Dget_create_plist(dset);
        
        switch (H5Pget_layout(plist)) {
          case H5D_COMPACT:
            mxSetField(dataset_structure, i, "Layout", 
                       mxCreateString("compact"));
            break;
          case H5D_CONTIGUOUS:
            mxSetField(dataset_structure, i, "Layout", 
                       mxCreateString("contiguous"));
            break;
          case H5D_CHUNKED:
            mxSetField(dataset_structure, i, "Layout", 
                       mxCreateString("chunked"));
            /* Chunksize */
            
            break;
          default:
            errHandler("Unable to identify dataset layout");
        }
        /*
         * Need to get object info on everything in here
        member_info = get_object_info(fid, filename, fullname_group);
        */
        H5Gget_objinfo(dset, ".", true, &statbuf);

        /*
         * Links - iterate and get links.
	 */

        /* Attributes - iterate and get attributes.  This should be in it's own function.*/

	attribute_info.num_attributes = H5Aget_num_attrs(dset);


	if (attribute_info.num_attributes > 0)
	{
	    int num_attribute_no_value_fields;
	    int num_attribute_fields;

	    attribute_info.attribute_names = (char **) mxMalloc(attribute_info.num_attributes * sizeof(char *));
	    status = H5Aiterate(dset, NULL, populate_attributes, &attribute_info);

        /*
	 * Change this to read attribute values if the user desires!
	 */
	if (read_attribute_values)
	{
	    num_attribute_fields = GET_NUMBER_FIELDNAMES(attribute_fields);
        
	    attribute_structure = mxCreateStructMatrix(1, attribute_info.num_attributes,
							num_attribute_fields,
							attribute_fields);
	}else
	{
	    num_attribute_no_value_fields = GET_NUMBER_FIELDNAMES(attribute_no_value_fields);
        
	    attribute_structure = mxCreateStructMatrix(1, attribute_info.num_attributes,
							num_attribute_no_value_fields,
						    attribute_no_value_fields);
	}
        for (j = 0; j < attribute_info.num_attributes; j++)
        {
            hid_t attribute;
            hid_t dspace;
	    hid_t atype;
	    char *fullname_attribute;

	    /*
	     * Set filename
	     */

	    mxSetField(attribute_structure, j, "Filename", mxCreateString(filename));

	    /*
	     * Set attribute name
	     */

	    fullname_attribute = (char *) mxMalloc((strlen(fullname_dataset) + 1 + strlen(attribute_info.attribute_names[j]) * sizeof(char)) + 1);
	    strcpy(fullname_attribute, fullname_dataset);
	    strcat(fullname_attribute, "/");
	    strcat(fullname_attribute, attribute_info.attribute_names[j]);

            mxSetField(attribute_structure, j, "Name", mxCreateString(fullname_attribute));
            attribute = open_attribute(dset, attribute_info.attribute_names[j]);
            dspace = get_dataspace(attribute, true);
	    /*
	     * Set value.  First, get datatype, and dataspace.
	     */
	    atype = get_datatype(attribute, true);

	    if (!read_attribute_values)
	    {
		mxSetField(attribute_structure, j, "Rank", mxCreateDoubleScalar(get_rank(dspace)));
		mxSetField(attribute_structure, j, "Dims", get_dims(dspace));
		mxSetField(attribute_structure, j, "MaxDims", get_max_dims(dspace));
		mxSetField(attribute_structure, j, "Datatype", create_datatype_structure(atype));
	    }
	    else
	    {
		mxArray *ml_attr_val;
		mxArray *ml_attr;
		char *attr_val;
/*
		ml_attr_val = read_attribute(fid, fullname_attribute);
*/

		hid_t atype = H5Aget_type(attribute);

		int ndims;
		hsize_t size[H5S_MAX_RANK];
		int *mldims;
		int mlrank = get_ml_rank(ndims);		


		ndims = H5Sget_simple_extent_dims(dspace, size, NULL);
		mlrank = get_ml_rank(ndims);
		mldims = get_ml_dims(mlrank, ndims, size);
		
		attr_val = read_attribute(attribute, dspace, atype, ndims, mlrank, mldims, &ml_attr);
		ml_attr_val = convert_data_to_ML(dspace, atype, ndims, mlrank, mldims, ml_attr, attr_val);

		if ((strncmp(attribute_info.attribute_names[j], "CLASS", 5) == 0) && H5Tequal(H5Tget_class(atype), H5T_STRING))
		{
		    char attr_val[5];
		    mxGetString(ml_attr_val, attr_val, 5);	
		    if (strncmp((const char *) attr_val, "image", 5))
		    {
			isimage = true;
		    }
		}
		mxSetField(attribute_structure, j, "Value", ml_attr_val);
	    }
	    /*
	     * Close all of the ids
	     */
	    close_datatype(atype);
            close_dataspace(dspace);
            close_attribute(attribute);
	    mxFree(fullname_attribute);
        }

	mxSetField(dataset_structure, i, "Attributes", attribute_structure);
	
	
	free_heap_memory(&attribute_info);
	

        /* Is Image - this value can only be set if the attributes are read! */
/*
	if (isimage)
	{
	    mxSetField(dataset_structure, i, "IsImage", mxCreateLogicalScalar(true));
	}
*/
	}


        /* FillValue - should we even leave this in? */

	dtype = H5Dget_type(dset);
	mltype = HDF5_to_ML_datatype(dtype);

	/*
	 * Fill values are always scalar, no? 
	 */
	fill_value = mxCreateNumericMatrix(1, 1, mltype, mxREAL); 
	fvalue = mxGetData(fill_value);
	fvalue = mxMalloc(1 * H5Tget_size(dtype));
	if (H5Pget_fill_value(plist, dtype, fvalue) >= 0)
	{
	    mxSetField(dataset_structure, i, "FillValue", fill_value);
	} 
	else
	{
	    mxFree(fvalue);
	    mxDestroyArray(fill_value);
	}

        close_datatype(ginfo->datatype_class[i]);
        H5Pclose(plist);
        close_dataspace(dspace);
        close_dataset(dset);
        close_group(loc_id);
	mxFree(fullname_dataset);
    }
    return dataset_structure;
}


/*
 * Returns an attribute structure.
 */
mxArray * create_attribute_structure(hid_t fid, const char *filename, const char *group_name, ginfo_ptr ginfo, 
                                  bool read_attribute_values)
{
    mxArray *attribute_structure;
    int num_attribute_fields;
    int num_attribute_no_value_fields;
    int i;
    hid_t loc_id;

    /*
     * loc_id is the group id
     */
    loc_id = open_group(fid, group_name);

    if (read_attribute_values)
    {
        /*
         * If we're reading the attribute values, the struct array
         * has only 2 fields per attribute
         */
        num_attribute_fields = GET_NUMBER_FIELDNAMES(attribute_fields);
        attribute_structure = mxCreateStructMatrix(1, ginfo->num_attributes,
                                                   num_attribute_fields,
                                                   attribute_fields);
        for (i = 0; i < ginfo->num_attributes; i++)
        {
	    mxArray *ml_attr_val;
	    char *fullname_attribute;
	    char *attr_val;
	    hid_t atype;
	    int ndims;
	    hsize_t size[H5S_MAX_RANK];
	    int *mldims;
	    int mlrank = get_ml_rank(ndims);		
	    hid_t loc_id, attribute;
	    hid_t dspace;
	    mxArray *ml_attr;


	    mxSetField(attribute_structure, i, "Filename", mxCreateString(filename));

	    /* Add 1 for NULL and 1 for the / */
	    if (strcmp(group_name, "/") != 0)
	    {
		fullname_attribute = mxMalloc((strlen(group_name) + strlen(ginfo->attribute_names[i]) + 1 + 1) * sizeof(char));
		strcpy(fullname_attribute, group_name); 
		strcat(fullname_attribute,"/");
		strcat(fullname_attribute, ginfo->attribute_names[i]);
	    }else
	    {
		fullname_attribute = mxMalloc((strlen(group_name) + strlen(ginfo->attribute_names[i]) + 1) * sizeof(char));
		strcpy(fullname_attribute, group_name);
		strcat(fullname_attribute, ginfo->attribute_names[i]);
	    }

	    mxSetField(attribute_structure, i, "Name", mxCreateString(fullname_attribute));

            /*
             * loc_id must be the fullname, not just the group name
             * where the attribute lives
             */

	    loc_id = open_location(fid, fullname_attribute);

	    attribute = open_attribute(loc_id, ginfo->attribute_names[i]);    

	    atype = get_datatype(attribute, true);
	    dspace =  get_dataspace(attribute, true);
	    ndims = H5Sget_simple_extent_dims(dspace, size, NULL);
	    mlrank = get_ml_rank(ndims);
	    mldims = get_ml_dims(mlrank, ndims, size);

	    attr_val = read_attribute(attribute, dspace, atype, ndims, mlrank, mldims, &ml_attr);
	    ml_attr_val = convert_data_to_ML(dspace, atype, ndims, mlrank, mldims, ml_attr, attr_val);

	    mxSetField(attribute_structure, i, "Value", ml_attr_val);
        }
    } else
    {
        num_attribute_no_value_fields = GET_NUMBER_FIELDNAMES(attribute_no_value_fields);
        
        attribute_structure = mxCreateStructMatrix(1, ginfo->num_attributes,
                                                   num_attribute_no_value_fields,
                                                   attribute_no_value_fields);
        for (i = 0; i < ginfo->num_attributes; i++)
        {
            hid_t attribute;
            hid_t dspace;
	    hid_t atype;
	    char *fullname_attribute;

	    mxSetField(attribute_structure, i, "Filename", mxCreateString(filename));

	    /* Add 1 for NULL and 1 for the / */
	    if (strcmp(group_name, "/") != 0)
	    {
		fullname_attribute = mxMalloc((strlen(group_name) + strlen(ginfo->attribute_names[i]) + 1 + 1) * sizeof(char));
		strcpy(fullname_attribute, group_name); 
		strcat(fullname_attribute,"/");
		strcat(fullname_attribute, ginfo->attribute_names[i]);
	    }else
	    {
		fullname_attribute = mxMalloc((strlen(group_name) + strlen(ginfo->attribute_names[i]) + 1) * sizeof(char));
		strcpy(fullname_attribute, group_name);
		strcat(fullname_attribute, ginfo->attribute_names[i]);
	    }

	    /* Name */
	    mxSetField(attribute_structure, i, "Name", mxCreateString(fullname_attribute));

            /*Rank*/
            attribute = open_attribute(loc_id, ginfo->attribute_names[i]);
            dspace = get_dataspace(attribute, true);
	    atype = get_datatype(attribute, true);
            mxSetField(attribute_structure, i, "Rank", mxCreateDoubleScalar(get_rank(dspace)));
            /* Datatype*/
	    mxSetField(attribute_structure, i, "Datatype", create_datatype_structure(atype));
            /* Dims*/
            mxSetField(attribute_structure, i, "Dims", get_dims(dspace));
            /*MaxDims*/
            mxSetField(attribute_structure, i, "MaxDims", get_max_dims(dspace));
            close_dataspace(dspace);
            close_attribute(attribute);
	    close_datatype(atype);
        }
    }

    close_group(loc_id);
    return attribute_structure;
}


/*
 * Creates a datatype structure to be returned through the info function.  This
 * is specific to named datatypes
 */
mxArray * create_named_datatype_structure(hid_t fid, const char *filename, const char *group_name, ginfo_ptr ginfo)
{
    mxArray *datatype_structure;
    int num_datatype_fields; /* number of fields for datatypes */
    int i;

    num_datatype_fields = GET_NUMBER_FIELDNAMES(datatype_fields);

    datatype_structure = mxCreateStructMatrix(1, ginfo->num_datatypes,
                                             num_datatype_fields,
                                             datatype_fields);
    for (i = 0; i < ginfo->num_datatypes; i++)
    {
	char *fullname_datatype;
	hid_t dtype;
        int elem_counter, num_elements;
	mxArray *elements;
	int elem_dims[2];

	/*
         * Populate the datatype structure (array)
         */
	    
	/* Add 1 for NULL and 1 for the / */
	if (strcmp(group_name, "/") != 0)
	{
	    fullname_datatype = mxMalloc((strlen(group_name) + strlen(ginfo->named_datatype_names[i]) + 1 + 1) * sizeof(char));
	    strcpy(fullname_datatype, group_name); 
	    strcat(fullname_datatype,"/");
	    strcat(fullname_datatype, ginfo->named_datatype_names[i]);
	}else
	{
	    fullname_datatype = mxMalloc((strlen(group_name) + strlen(ginfo->named_datatype_names[i]) + 1) * sizeof(char));
	    strcpy(fullname_datatype, group_name);
	    strcat(fullname_datatype, ginfo->named_datatype_names[i]);
	}

	mxSetField(datatype_structure, i, "Name", mxCreateString(fullname_datatype));

        /*
         * Return the class.  Open the datatype, get the class, etc.  Lord!
         */

	dtype = H5Topen(fid, fullname_datatype);
	AddIDToList(dtype, DatatypeList);

        switch (H5Tget_class(dtype))
	{
          case H5T_INTEGER:
            mxSetField(datatype_structure, i, "Class", mxCreateString("H5T_INTEGER"));
            break;
          case H5T_FLOAT:
            mxSetField(datatype_structure, i, "Class", mxCreateString("H5T_FLOAT"));
            break;
          case H5T_TIME:
            mxSetField(datatype_structure, i, "Class", mxCreateString("H5T_TIME"));
            break;
          case H5T_STRING:
            mxSetField(datatype_structure, i, "Class", mxCreateString("H5T_STRING"));
            break;
          case H5T_BITFIELD:
            mxSetField(datatype_structure, i, "Class", mxCreateString("H5T_BITFIELD"));
            break;
          case H5T_OPAQUE:
            mxSetField(datatype_structure, i, "Class", mxCreateString("H5T_OPAQUE"));
            break;
          case H5T_COMPOUND:
            mxSetField(datatype_structure, i, "Class", mxCreateString("H5T_COMPOUND"));
            break;
          case H5T_REFERENCE:
            mxSetField(datatype_structure, i, "Class", mxCreateString("H5T_REFERENCE"));
            break;
          case H5T_ENUM:
            mxSetField(datatype_structure, i, "Class", mxCreateString("H5T_ENUM"));

	    /*
	     * Return the elements in this named datatype.  Need to do something different for compound types...
	     */
	    num_elements = H5Tget_nmembers(dtype);
	    elem_dims[0] = num_elements;
	    elem_dims[1] = 2; /* Second column is for value */

	    elements = mxCreateCellArray(2, elem_dims);

	    for (elem_counter = 0; elem_counter < num_elements; elem_counter++)
	    {
		char *key = H5Tget_member_name(dtype, elem_counter);
		void *value;
		mxArray *ml_value;
		mxClassID mltype;

		mltype = HDF5_to_ML_datatype(dtype);
		ml_value = mxCreateNumericMatrix(1, 1, mltype, mxREAL);
		value = mxMalloc(1 * sizeof(short));
		value = mxGetData(ml_value);

		H5Tget_member_value(dtype, elem_counter, value);

		mxSetCell(elements, elem_counter, mxCreateString(key));
		mxSetCell(elements, elem_counter + num_elements, ml_value);
	    }

	    mxSetField(datatype_structure, i, "Elements", elements);

            break;
          case H5T_VLEN:
            mxSetField(datatype_structure, i, "Class", mxCreateString("H5T_VLEN"));
            break;
          case H5T_ARRAY:
            mxSetField(datatype_structure, i, "Class", mxCreateString("H5T_ARRAY"));
            break;
        }

	close_datatype(dtype);
	mxFree(fullname_datatype);

    /* You can have attributes in a named datatype structure (???)
     *
     */
    }
    return datatype_structure;
}
/*
 * Creates a link structure to be returned through the info struct.  Right now it only
 * works on softlinks, thanks to HDF5 being weird about hardlinks.
 */
mxArray * create_link_structure(hid_t fid, const char *group_name, ginfo_ptr ginfo, bool ishardlink, const char* hardlink_target)
{
    mxArray *link_structure;
    int num_link_fields; /* number of fields for links */

    num_link_fields = sizeof(link_fields) / sizeof(*link_fields);


    if (ishardlink)
    {
	link_structure = mxCreateStructMatrix(1, 1, num_link_fields, link_fields);
	mxSetField(link_structure, 0, "Name", mxCreateString(group_name));
	mxSetField(link_structure, 0, "IsHardLink", mxCreateLogicalScalar(ishardlink));
	mxSetField(link_structure, 0, "Target", mxCreateString(hardlink_target));
    }else
    {
        int i;
	hid_t loc_id;

	loc_id = open_group(fid, group_name);

        link_structure = mxCreateStructMatrix(1, ginfo->num_links,
                                             num_link_fields,
                                             link_fields);
	for (i = 0; i < ginfo->num_links; i++)
	{
	    H5G_stat_t statbuf;
	    char * target_name;
        
            H5Gget_objinfo(loc_id, ginfo->link_names[i], false, &statbuf);
	    target_name = mxMalloc((statbuf.linklen + 1) * sizeof(char));
	    H5Gget_linkval(loc_id, ginfo->link_names[i], statbuf.linklen, target_name);
	    /*
	     * Populate the link structure (array)
	     */
	    mxSetField(link_structure, i, "Name", mxCreateString(ginfo->link_names[i]));
	    mxSetField(link_structure, i, "IsHardLink", mxCreateLogicalScalar(ishardlink));
	    mxSetField(link_structure, i, "Target", mxCreateString(target_name));
	    mxFree(target_name);
	}
	close_group(loc_id);
    }
    return link_structure;
}

/*
 * Creates a group structure that is to be returned through the info function
 */
mxArray * create_group_structure(hid_t fid, char *filename, char *group_name, group_info ginfo, bool read_attribute_values)
{
    int i;
    int num_group_fields; /* number of fields for groups */
    mxArray *group_structure;
    group_info member_info;

    num_group_fields = GET_NUMBER_FIELDNAMES(group_fields);

    if (ginfo.num_groups > 0)
    {
        group_structure = mxCreateStructMatrix(1, ginfo.num_groups,
                                               num_group_fields, group_fields);
    }else
    {
        group_structure = NULL;
    }

    for (i = 0; i < ginfo.num_groups; i++)
    {
        char *member_group_name;
	char *fullname_group;
	mxArray *member_group;
	bool ishardlink = false;
        
        member_group_name = ginfo.group_names[i];

        if (strcmp(group_name, "/") != 0)
        {
            fullname_group = mxMalloc((strlen(member_group_name) + strlen(group_name) + 1 + 1) * sizeof(char));
            strcpy(fullname_group, group_name); 
            strcat(fullname_group,"/");
            strcat(fullname_group, member_group_name);
        }else
        {
            fullname_group = mxMalloc((strlen(member_group_name) + strlen(group_name) + 1) * sizeof(char));
            strcpy(fullname_group, group_name);
            strcat(fullname_group, member_group_name);
        }
        
        /* 
         * Get the group structure
         */
        member_info = get_object_info(fid, filename, fullname_group);

	/* If we have a hard link, we have to return a link structure
	 */
	if (ginfo.nlink[i] > 1)
	{
	    int index_to_linked_obj;
	    index_to_linked_obj = find_entry(ginfo.objno[i]);
	    if (index_to_linked_obj < 0) {
		insert_entry(ginfo.objno[i], ginfo.group_names[i]);
	    }
	    else
	    {
		/* We have a hard link and should return a link structure.
		*/
		member_group = create_link_structure(fid, fullname_group, NULL, true, table[index_to_linked_obj].objname);
		mxSetField(group_structure, i, "Links", member_group);
		mxFree(fullname_group);
		ishardlink = true;
		break;
	    }
	}
	if (!ishardlink)
	{
	    member_group = create_group_structure(fid, filename, fullname_group, member_info, read_attribute_values);
	    mxSetField(group_structure, i, "Groups", member_group);
	    mxSetField(group_structure, i, "Filename", mxCreateString(filename));
	    mxSetField(group_structure, i, "Name", mxCreateString(fullname_group));
	}

        /*
         * Instead of this, do the NULL check in create_dataset_structure
         */
        if (member_info.num_datasets > 0)
        {
            mxArray * dataset_structure;
            
            dataset_structure = create_dataset_structure(fid, fullname_group, filename, &member_info, read_attribute_values);
            mxSetField(group_structure, i, "Datasets", dataset_structure);
        }
        
        /*
         * Get attribute structure
         */
        if (member_info.num_attributes > 0)
        {
            mxArray * attribute_structure;
            
            attribute_structure = create_attribute_structure(fid, filename, fullname_group, &member_info, read_attribute_values);
            mxSetField(group_structure, i, "Attributes", attribute_structure);
        }
        
        /*
         * Get datatype structure
         */
        if (member_info.num_datatypes > 0)
        {
            mxArray * datatype_structure;
            
            datatype_structure = create_named_datatype_structure(fid, filename, fullname_group, &member_info);
            mxSetField(group_structure, i, "Datatypes", datatype_structure);
        }
        
        /*
         * Links are really soft links.  Hard links aren't defined as
         * a "object type" - rather, that just refers to objects that are
         * hard linked.
         */
        if (member_info.num_links > 0)
        {
            mxArray * link_structure;
            
            link_structure = create_link_structure(fid, fullname_group, &member_info, false, NULL);
            mxSetField(group_structure, i, "Links", link_structure);
        }

	free_heap_memory(&member_info);
        mxFree(fullname_group);

    } /* end for */
    
    return group_structure;
}


/*
 * Returns an character array mxArray when passed
 * an HDF5 datatype.
 */

mxArray * datatype_to_description(hid_t datatype_id)
{
    switch (H5Tget_class(datatype_id)) {
    case H5T_INTEGER:
	if (H5Tequal(datatype_id, H5T_STD_I8BE)) {
	    return mxCreateString("H5T_STD_I8BE");
	} else if (H5Tequal(datatype_id, H5T_STD_I8LE)) {
	    return mxCreateString("H5T_STD_I8LE");
	} else if (H5Tequal(datatype_id, H5T_STD_I16BE)) {
	    return mxCreateString("H5T_STD_I16BE");
	} else if (H5Tequal(datatype_id, H5T_STD_I16LE)) {
	    return mxCreateString("H5T_STD_I16LE");
	} else if (H5Tequal(datatype_id, H5T_STD_I32BE)) {
	    return mxCreateString("H5T_STD_I32BE");
	} else if (H5Tequal(datatype_id, H5T_STD_I32LE)) {
	    return mxCreateString("H5T_STD_I32LE");
	} else if (H5Tequal(datatype_id, H5T_STD_I64BE)) {
	    return mxCreateString("H5T_STD_I64BE");
	} else if (H5Tequal(datatype_id, H5T_STD_I64LE)) {
	    return mxCreateString("H5T_STD_I64LE");
	} else if (H5Tequal(datatype_id, H5T_STD_U8BE)) {
	    return mxCreateString("H5T_STD_U8BE");
	} else if (H5Tequal(datatype_id, H5T_STD_U8LE)) {
	    return mxCreateString("H5T_STD_U8LE");
	} else if (H5Tequal(datatype_id, H5T_STD_U16BE)) {
	    return mxCreateString("H5T_STD_U16BE");
	} else if (H5Tequal(datatype_id, H5T_STD_U16LE)) {
	    return mxCreateString("H5T_STD_U16LE");
	} else if (H5Tequal(datatype_id, H5T_STD_U32BE)) {
	    return mxCreateString("H5T_STD_U32BE");
	} else if (H5Tequal(datatype_id, H5T_STD_U32LE)) {
	    return mxCreateString("H5T_STD_U32LE");
	} else if (H5Tequal(datatype_id, H5T_STD_U64BE)) {
	    return mxCreateString("H5T_STD_U64BE");
	} else if (H5Tequal(datatype_id, H5T_STD_U64LE)) {
	    return mxCreateString("H5T_STD_U64LE");
	} else if (H5Tequal(datatype_id, H5T_NATIVE_SCHAR)) {
	    return mxCreateString("H5T_NATIVE_SCHAR");
	} else if (H5Tequal(datatype_id, H5T_NATIVE_UCHAR)) {
	    return mxCreateString("H5T_NATIVE_UCHAR");
	} else if (H5Tequal(datatype_id, H5T_NATIVE_SHORT)) {
	    return mxCreateString("H5T_NATIVE_SHORT");
	} else if (H5Tequal(datatype_id, H5T_NATIVE_USHORT)) {
	    return mxCreateString("H5T_NATIVE_USHORT");
	} else if (H5Tequal(datatype_id, H5T_NATIVE_INT)) {
	    return mxCreateString("H5T_NATIVE_INT");
	} else if (H5Tequal(datatype_id, H5T_NATIVE_UINT)) {
	    return mxCreateString("H5T_NATIVE_UINT");
	} else if (H5Tequal(datatype_id, H5T_NATIVE_LONG)) {
	    return mxCreateString("H5T_NATIVE_LONG");
	} else if (H5Tequal(datatype_id, H5T_NATIVE_ULONG)) {
	    return mxCreateString("H5T_NATIVE_ULONG");
	} else if (H5Tequal(datatype_id, H5T_NATIVE_LLONG)) {
	    return mxCreateString("H5T_NATIVE_LLONG");
	} else if (H5Tequal(datatype_id, H5T_NATIVE_ULLONG)) {
	    return mxCreateString("H5T_NATIVE_ULLONG");
	} else {
	    errHandler("Encountered unknown integer type");
	}
	break;

    case H5T_FLOAT:
	if (H5Tequal(datatype_id, H5T_IEEE_F32BE)) {
	    return mxCreateString("H5T_IEEE_F32BE");
	} else if (H5Tequal(datatype_id, H5T_IEEE_F32LE)) {
	    return mxCreateString("H5T_IEEE_F32LE");
	} else if (H5Tequal(datatype_id, H5T_IEEE_F64BE)) {
	    return mxCreateString("H5T_IEEE_F64BE");
	} else if (H5Tequal(datatype_id, H5T_IEEE_F64LE)) {
	    return mxCreateString("H5T_IEEE_F64LE");
	} else if (H5Tequal(datatype_id, H5T_NATIVE_FLOAT)) {
	    return mxCreateString("H5T_NATIVE_FLOAT");
	} else if (H5Tequal(datatype_id, H5T_NATIVE_DOUBLE)) {
	    return mxCreateString("H5T_NATIVE_DOUBLE");
	} else if (H5Tequal(datatype_id, H5T_NATIVE_LDOUBLE)) {
	    return mxCreateString("H5T_NATIVE_LDOUBLE");
	} else {
	    errHandler("Encountered unknown float type");
	}
	break;

    case H5T_TIME:
	
	break;

    case H5T_STRING:
	return mxCreateString("H5T_STRING");
	break;

    case H5T_BITFIELD:
	if (H5Tequal(datatype_id, H5T_STD_B8BE)) {
	    return mxCreateString("H5T_STD_B8BE");
	} else if (H5Tequal(datatype_id, H5T_STD_B8LE)) {
	    return mxCreateString("H5T_STD_B8LE");
	} else if (H5Tequal(datatype_id, H5T_STD_B16BE)) {
	    return mxCreateString("H5T_STD_B16BE");
	} else if (H5Tequal(datatype_id, H5T_STD_B16LE)) {
	    return mxCreateString("H5T_STD_B16LE");
	} else if (H5Tequal(datatype_id, H5T_STD_B32BE)) {
	    return mxCreateString("H5T_STD_B32BE");
	} else if (H5Tequal(datatype_id, H5T_STD_B32LE)) {
	    return mxCreateString("H5T_STD_B32LE");
	} else if (H5Tequal(datatype_id, H5T_STD_B64BE)) {
	    return mxCreateString("H5T_STD_B64BE");
	} else if (H5Tequal(datatype_id, H5T_STD_B64LE)) {
	    return mxCreateString("H5T_STD_B64LE");
	} else {
	    errHandler("Encountered unknown bitfield datatype");
	}
	break;

    case H5T_OPAQUE:
	return mxCreateString("H5T_OPAQUE");
	break;
    

    case H5T_COMPOUND:
	return mxCreateString("H5T_COMPOUND"); 
	break;

    case H5T_REFERENCE:
	return mxCreateString("H5T_REFERENCE");
	break;

    case H5T_ENUM:
    	return mxCreateString("H5T_ENUM");
	/*
	 * Set the Elements/Members field 
	 */
	break;

    case H5T_VLEN:
	return mxCreateString("H5T_VLEN");
	/* Print the base type somehow.  Nothing more here.
	*/

	break;

    case H5T_ARRAY:
	/* Name should be the base type, maybe? */

	return mxCreateString("H5T_ARRAY");
	break;

    default:
	errHandler("Encountered unknown datatype");
	return mxCreateString(""); /* bogus - to eliminate compiler warning */
	break;
    }
    return mxCreateString(""); /* bogus - to eliminate compiler warning */
}
