/*

  This is an example that shows how to use new Error handling APIs.
  
 */
#define DIM0    100
#define DIM1    200
#define FAKE_ID 0

#include <stdlib.h>
#include <string.h>
#include "hdf5.h"

const char *FILENAME[] = {
    "errors.h5",
    NULL
};


hid_t   ERR_CLS;

hid_t   ERR_MAJ_TEST;
hid_t   ERR_MAJ_IO;


hid_t   ERR_MIN_MYFUNC;
hid_t   ERR_MIN_CREATE;
hid_t   ERR_MIN_WRITE;

#define DSET_NAME               "a_dataset"
#define FAKE_ID                 0

#define ERR_CLS_NAME            "Error Test"
#define PROG_NAME               "Error Program"
#define PROG_VERS               "1.0"

#define ERR_MAJ_TEST_MSG             "Error in test"
#define ERR_MAJ_IO_MSG               "Error in IO"
#define ERR_MIN_MYFUNC_MSG           "Error in my function"
#define ERR_MIN_CREATE_MSG           "Error in H5Dcreate"
#define ERR_MIN_WRITE_MSG            "Error in H5Dwrite"


/*
 * Print the current location on the standard output stream.
 */
#define AT() 		printf ("	 at %s:%d in %s()...\n",	      \
				__FILE__, __LINE__, __FUNCTION__);
#define H5_FAILED()	{puts("*FAILED*");fflush(stdout);}
#define ERROR      {H5_FAILED(); AT(); goto error;}


/*-------------------------------------------------------------------------
 * Function:	init_error
 *
 * Purpose:	Initialize error information.
 *
 *-------------------------------------------------------------------------
 */
static herr_t
init_error(void)
{
  ssize_t cls_size = (ssize_t)strlen(ERR_CLS_NAME)+1;
  char *cls_name = malloc(strlen(ERR_CLS_NAME)+1);
  ssize_t msg_size = (ssize_t)strlen(ERR_MIN_MYFUNC_MSG) + 1;
  char *msg = malloc(strlen(ERR_MIN_MYFUNC_MSG)+1);
  H5E_type_t *msg_type= malloc(sizeof(H5E_type_t));
  
  
  if((ERR_CLS = H5Eregister_class(ERR_CLS_NAME, PROG_NAME, PROG_VERS)) < 0)
    ERROR;

  if(cls_size != H5Eget_class_name(ERR_CLS, cls_name, (size_t)cls_size) + 1)
    ERROR;

  if((ERR_MAJ_TEST = H5Ecreate_msg(ERR_CLS, H5E_MAJOR, ERR_MAJ_TEST_MSG)) < 0)
    ERROR;
  if((ERR_MAJ_IO = H5Ecreate_msg(ERR_CLS, H5E_MAJOR, ERR_MAJ_IO_MSG)) < 0)
    ERROR;
  
  if((ERR_MIN_MYFUNC = H5Ecreate_msg(ERR_CLS, H5E_MINOR, ERR_MIN_MYFUNC_MSG)) < 0)
    ERROR;
  if((ERR_MIN_CREATE = H5Ecreate_msg(ERR_CLS, H5E_MINOR, ERR_MIN_CREATE_MSG)) < 0)
    ERROR;
    if((ERR_MIN_WRITE = H5Ecreate_msg(ERR_CLS, H5E_MINOR, ERR_MIN_WRITE_MSG)) < 0)
    ERROR;

  
  if(msg_size != H5Eget_msg(ERR_MIN_MYFUNC, msg_type, msg, (size_t)msg_size) + 1)
    ERROR;
  if(*msg_type != H5E_MINOR)
    ERROR;
  if(strcmp(msg, ERR_MIN_MYFUNC_MSG))
    ERROR;
  
  free(cls_name);
  free(msg);
  free(msg_type);
  
  return 0;

error:
    return -1;
} 

/* end init_error() */


/*-------------------------------------------------------------------------
 * Function:	my_function
 *
 * Purpose:	Manipulates current error stack.
 *
 *-------------------------------------------------------------------------
 */
static herr_t
my_function(hid_t fid)
{
    hid_t estack_id;   
    hid_t		dataset, space;
    hsize_t		dims[2];
    const char          *FUNC_my_function = "my_function";

    
    /* Create the data space */
    dims[0] = DIM0;
    dims[1] = DIM1;
    
    if ((space = H5Screate_simple(2, dims, NULL))<0)
      ERROR;

    /* Force this function to fail and make it push error */
    H5E_BEGIN_TRY {
        dataset = H5Dcreate(FAKE_ID, DSET_NAME, H5T_STD_I32BE, space, H5P_DEFAULT);
    } H5E_END_TRY;

    if(dataset < 0) {
        H5Epush2(H5E_DEFAULT, __FILE__, FUNC_my_function, __LINE__, ERR_CLS, ERR_MAJ_IO, ERR_MIN_CREATE,
                "H5Dcreate failed");
	/* Test saving and restoring the current error stack */
	estack_id = H5Eget_current_stack();
	H5Dclose(dataset);
	H5Sclose(space);	
	H5Eset_current_stack(estack_id); /* Try to uncomment this to see the difference in the result. */ 
        goto error;
    } /* end if */

    if(H5Sclose(space) < 0)
        ERROR;


    return 0;

error:
    return -1;
} /* end my_function() */


/*-------------------------------------------------------------------------
 * Function:	close_error
 *
 * Purpose:	Closes error information.
 *
 *-------------------------------------------------------------------------
 */
static herr_t
close_error(void)
{
  /* Close major errors, let H5Eunregister_class close minor errors */
  if(H5Eclose_msg(ERR_MAJ_TEST) < 0)
    ERROR;

  if(H5Eclose_msg(ERR_MAJ_IO) < 0)
    ERROR;


  /* let H5Eunregister_class close major and minor error and class handles */
  if(H5Eunregister_class(ERR_CLS) < 0)
    ERROR;

    return 0;

error:
    return -1;
} /* end close_error() */


/*-------------------------------------------------------------------------
 * Function:	main
 *
 * Purpose:	Test error API.
 *
 *-------------------------------------------------------------------------
 */
int
main(void)
{
  const char *FUNC_main = "main";
  H5E_auto2_t old_func;
  hid_t file;
  hid_t estack_id;
  
  int ipoints2[DIM0][DIM1];
  int err_num;
  
  unsigned is_v2 = 1; /* flag for error stack type */

  void *old_data;

    
  /* Initialize errors class and messages */
  if(init_error() < 0)
    ERROR;

  if((file = H5Fcreate(FILENAME[0], H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0)
    ERROR;

  
  
  if(my_function(file) < 0) {
    /* Push an error onto error stack */
    if(H5Epush2(H5E_DEFAULT, __FILE__, FUNC_main, __LINE__, ERR_CLS, ERR_MAJ_TEST, ERR_MIN_MYFUNC,
		"Error stack test failed") < 0)
      ERROR;
    if((err_num = H5Eget_num(H5E_DEFAULT)) < 0){
      ERROR;
    }
    else{
      fprintf(stderr, "Error Stack has now %d errors.\n", err_num);
    }
    /* Print out the errors on stack */
    if(H5Eprint2(H5E_DEFAULT, stderr) < 0)
      ERROR;
    H5Epop(H5E_DEFAULT, 2);
    if((err_num = H5Eget_num(H5E_DEFAULT)) < 0){
      ERROR;
    }
    else{
      fprintf(stderr, "Error Stack has now %d errors.\n", err_num);
    }
    /* Print out the errors on stack */
    if(H5Eprint2(H5E_DEFAULT, stderr) < 0)
      ERROR;
    
    /* Test enabling and disabling default printing. */
    if(H5Eget_auto2(H5E_DEFAULT, &old_func, &old_data) < 0)
     ERROR;
    /* Disable printing */ 
    if(H5Eset_auto2(H5E_DEFAULT, NULL, NULL) < 0)
        ERROR;

    /* Make H5Dwrite fail, verify default print is disabled */
    if(H5Dwrite(FAKE_ID, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, ipoints2) >= 0) {
      H5Epush2(H5E_DEFAULT, __FILE__, FUNC_main, __LINE__, ERR_CLS, ERR_MAJ_IO, ERR_MIN_WRITE,
	      "H5Dwrite shouldn't succeed");
      goto error;
    }
    
    fprintf(stderr, "Re-enabling error printing\n");
    /* Re-enable printing */ 
    if(H5Eset_auto2(H5E_DEFAULT, old_func, old_data) < 0)
      ERROR;

    /* Make H5Dwrite fail, verify default print is disabled */

    if(H5Dwrite(FAKE_ID, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, ipoints2) < 0) {
      H5Epush2(H5E_DEFAULT, __FILE__, FUNC_main, __LINE__, ERR_CLS, ERR_MAJ_IO, ERR_MIN_WRITE,
	      "H5Dwrite shouldn't succeed");
      /* Print out the errors on stack */
      if(H5Eprint2(H5E_DEFAULT, stderr) < 0)
	ERROR;      
    }  /* end if */
    
    /* Get the current error stack */
    estack_id = H5Eget_current_stack();
    /* Check the type of error stack */
    if(H5Eauto_is_v2(estack_id, &is_v2) <0)
      ERROR;
    if(is_v2 == 0){
      /* 0 means stack is H5E_auto_t type */
      fprintf(stderr, "The current error stack type is H5E_auto_t.\n");
    }
    else{
      /* 1 means stack is H5E_auto_stack_t type.  */
      fprintf(stderr, "The current error stack type is H5E_auto_stack_t.\n");
    }
    /* Empty error stack */
    if(H5Eclear2(estack_id)<0)
      ERROR;
	
    /* Close error stack */
    if(H5Eclose_stack(estack_id)<0)
      ERROR;
    
    /* Print out the errors on stack */
    if(H5Eprint2(H5E_DEFAULT, stderr) < 0)
      ERROR;
    
  
  } /* end if */

  if(H5Fclose(file) < 0)
    ERROR;

  /* Close error information */
  if(close_error() < 0)
    ERROR;

  return 0;

 error:
  return 1;
}
