/*######################################################################
  ######################################################################
  ########                                                      ########
  ########           C reader for ARPS HDF4 files               ########
  ########                                                      ########
  ######################################################################
  ######################################################################

!------------------------------------------------------------------------
!
!  PURPOSE:
!    To read ARPS HDF 4 history files.
!
!  Usage:
!
!    To compile:
!      $> h4cc -o readarps readarps.c
!
!    To run:
!      $> readarps [options] ARPS_HDF4_file_name
!
!      options are:
!        -n vname: print variable vname
!        -v      : verbose message
!        -h      : for help
!
!    To do meaningful job:
!      Please add your own code to the dataset loop below
!
!-----------------------------------------------------------------------
!
! Author:
!   Yunheng Wang (08/01/2005)
!
!----------------------------------------------------------------------
 */

#include "mfhdf.h"         // HDF header

int hdfrdi(int32 sd_id, char *attname, int32 *file_data) {
  int32  attr_index;
  intn   status;

  attr_index = SDfindattr(sd_id,attname);
  status     = SDreadattr(sd_id,attr_index,file_data);

  if (status < 0) {
    printf("\nERROR: attribute \"%s\" not found.\n",attname);
    *file_data = 1;
  }
  return status;
}

int hdfrdc(int32 sd_id, char *attname, int8 *file_data) {
  int32  attr_index;
  intn   status;

  attr_index = SDfindattr(sd_id,attname);
  status     = SDreadattr(sd_id,attr_index,file_data);

  if (status < 0) {
    printf("\nERROR: attribute \"%s\" not found.\n",attname);
    sprintf(file_data,"Unknown");
  }
  return status;
}

int hdfrdr(int32 sd_id, char *attname, float32 *file_data) {
  int32  attr_index;
  intn   status;

  attr_index = SDfindattr(sd_id,attname);
  status     = SDreadattr(sd_id,attr_index,file_data);

  if (status < 0) {
    printf("\nERROR: attribute \"%s\" not found.\n",attname);
    exit(0);
  }
  return status;
}

extern char *optarg;
extern int optind;


int main(int argc, char * argv[])
{

  /****************** Variable declaration *****************************/

  char    filename[128];
  char    runname[80],sizestr[80];
  int32   sd_id, sds_id;
  int32   n_datasets, n_file_attrs;
  int32   nx,ny,nz,nzsoil,nstyp,ns;

  int32   dindex;
  char    dname[MAX_NC_NAME];
  int32   rank, data_type, n_attrs;
  int32   dim_sizes[MAX_VAR_DIMS];
  intn    istatus;
  int     n,k,j,i,jd,kd,nd;
  int     ireturn;

  int32   start[MAX_VAR_DIMS],stride[MAX_VAR_DIMS];

  int32     bufsize;
  float32   amax, amin, fscale;
  float32 * buf_r;
  int32   * buf_i;
  int16   * bufc_i;
  float32 * buf_max, *buf_min;

  int   verbose;
  char  c, vname[40];

  /*@@@@@@@@@@@@@@@@@ Begin of executable code @@@@@@@@@@@@@@@@@@@@@@@*/

  ireturn = 0;
  verbose = 0;

  /****************** Get command line arguments **********************/

  while ((c = getopt(argc, argv, "n:hv")) != EOF) {
    switch (c) {
      case 'n': 
	sscanf(optarg,"%s",vname);
	break;
      case 'v':
	verbose = 1;
	break;
      case 'h':
      case '?':
	printf("\nUsage: %s [options] [ARPS HDF4 file name]\n",argv[0]);
	printf("\n    Options:\n");
	printf("       -n vname: Extract variable with name as \"vname\"\n");
	printf("       -v      : Print vebose message\n");
	printf("       -h      : Print usage and exit\n");
	printf("\n");
	return ireturn;
    }
  }
  
  if ( optind < argc ) {
    strcpy(filename,argv[optind]);
  } else {
    printf("Please enter the ARPS HDF4 history file name: ");
    scanf("%s",filename);
  }

  if(verbose) printf("\nREADARPS is about to read file: %s  ......\n",
                     filename);

  /****************** Open data file  ********************************/

  sd_id = SDstart(filename,DFACC_READ);
  if (sd_id == FAIL) {
    printf("ERROR: open HDF 4 file error. Program stopped.\n");
    return sd_id;
  }

  /****************** Get file attributes  ****************************/

  istatus = hdfrdc(sd_id,"runname", runname);
  if (verbose) printf("Runname for this file is: %s.\n",runname);

  istatus = hdfrdi(sd_id,"nx",    &nx);
  istatus = hdfrdi(sd_id,"ny",    &ny);
  istatus = hdfrdi(sd_id,"nz",    &nz);
  istatus = hdfrdi(sd_id,"nzsoil",&nzsoil);
  istatus = hdfrdi(sd_id,"nstyp", &nstyp);
  if (verbose) printf("Size of ARPS data are: nx = %d, ny = %d, nz = %d,"
         " nzsoil = %d, nstyp = %d.\n",nx,ny,nz,nzsoil,nstyp);

  ns = nstyp + 1;

  /****************** Allocate buffers  *******************************/

  bufsize = nx*ny*nz>nx*ny*nzsoil*ns?nx*ny*nz:nx*ny*nzsoil*ns;

  buf_r  = (float32 *) malloc(bufsize*sizeof(float32));
  buf_i  = (int32   *) malloc(bufsize*sizeof(int32));
  bufc_i = (int16   *) malloc(bufsize*sizeof(int16));

  buf_max = (float32 *) malloc(nz*sizeof(float32));
  buf_min = (float32 *) malloc(nz*sizeof(float32));

  /****************** Loop over all datasets  *************************/

  for (n=0;n<MAX_VAR_DIMS;n++) {
    start[n]  = 0;
    stride[n] = 1;
  }

  istatus = SDfileinfo(sd_id, &n_datasets, &n_file_attrs);
  if (verbose) printf("\nThere are totally %d datasets in the file\n",n_datasets);

  for(dindex=0;dindex<n_datasets;dindex++) {

    sds_id = SDselect(sd_id,dindex);

    istatus = SDgetinfo(sds_id, dname, &rank, dim_sizes, &data_type,
	                &n_attrs);
    if(verbose) {
      printf("\nREADARPS is reading variable: %s ",dname);
    
      if (rank == 1) {
        sprintf(sizestr,"%d",         dim_sizes[0]);
      } else if (rank == 2) {
        sprintf(sizestr,"%dx%d",      dim_sizes[0],dim_sizes[1]);
      } else if (rank == 3) {
        sprintf(sizestr,"%dx%dx%d",   dim_sizes[0],dim_sizes[1],
    	                              dim_sizes[2]);
      } else if (rank == 4) {
        sprintf(sizestr,"%dx%dx%dx%d",dim_sizes[0],dim_sizes[1],
  	                              dim_sizes[2],dim_sizes[3]);
      } else {
        printf(". Unsupport rank - %d --- Skipped.\n",rank);
        continue;
      }
    }

    /***************** 32-bit floating point without compression ******/

    if (data_type == DFNT_FLOAT32) {
      if(verbose) printf("(%dD, 32-bit floating point, %s) into buf_r",rank,sizestr);
      istatus = SDreaddata(sds_id,start,stride,dim_sizes,buf_r);

    /***************** 32-bit integer without compression ******/

    } else if (data_type == DFNT_INT32) {
      if(verbose) printf("(%dD, 32-bit signed integer, %s) into buf_i",rank,sizestr);
      istatus = SDreaddata(sds_id,start,stride,dim_sizes,buf_i);

    /***************** Compressed data *************************/

    } else if (data_type == DFNT_INT16) {
      if(verbose) printf("(%dD, 16-bit signed integer, %s) into bufc_i.\n",rank,sizestr);
      istatus = SDreaddata(sds_id,start,stride,dim_sizes,bufc_i);

      if (rank == 2) {
        bufsize =  dim_sizes[0]*dim_sizes[1];

	istatus = hdfrdr(sds_id,"max",&amax);
	istatus = hdfrdr(sds_id,"min",&amin);

	fscale = (amax - amin) / 65534.;
	for (i=0; i<bufsize;i++) {
	  buf_r[i] = fscale*(bufc_i[i]+32767) + amin;
	}

      } else if (rank == 3) {
        bufsize =  dim_sizes[1]*dim_sizes[2];

	istatus = hdfrdr(sds_id,"max",buf_max);
	istatus = hdfrdr(sds_id,"min",buf_min);
	for (k=0; k<dim_sizes[0];k++) {
	  fscale = (buf_max[k]-buf_min[k]) / 65534.;
	  kd = k*bufsize;
	  for (i=0; i<bufsize; i++) {
	    buf_r[i+kd] = fscale*(bufc_i[i+kd]+32767) + buf_min[k];
	  }
	}
      } else if (rank == 4) {
        bufsize =  dim_sizes[2]*dim_sizes[3];

	istatus = hdfrdr(sds_id,"max",buf_max);
	istatus = hdfrdr(sds_id,"min",buf_min);
	for (n=0; n < dim_sizes[0]; n++) {
	  nd = n*bufsize*dim_sizes[1];
	  for (k=0; k < dim_sizes[1]; k++) {
	    fscale = (buf_max[k]-buf_min[k]) / 65534.;
	    kd = k*bufsize;
	    for (i=0; i<bufsize; i++) {
	      buf_r[i+kd+nd] = fscale*(bufc_i[i+kd+nd]+32767) + buf_min[k];
	    }
	  }
	}

      }
      if(verbose) printf("         bufc_i was depacked into buf_r.");

    /***************** Unknown data type, Maybe error *********/

    } else {
      printf(". Unknown data type. Program stopped.\n");
      ireturn = -1;
      break;
    }

    if (istatus == FAIL) {
      if(verbose) printf(" --- ERROR!!!. Program stopped.\n");
      ireturn = istatus;
      break;
    } else {
      if(verbose) printf(" --- DONE.\n");
    }

    istatus = SDendaccess(sds_id);

    /*%%%%%%%%%%%%% Add your code below to do meaningful job %%%%%%%%%%*/

    /*=================================================================
     *
     * All data is now in eithe buf_r or buf_i
     *
     * soiltyp, vegtyp are in buf_i
     * all other data  is  in buf_r.
     *
     * You should add your own code to extract data from buf_r. The 
     * following example can be used as a template.
     *
     * ================================================================*/

    /*============= An example to print 3D 32-bit float  ==============
     *
     * Fortran ARPS array     C ARPS array    Data in buffer
     * ------------------     -------------   ---------------------
     *  var(i+1,j+1,k+1)   =  var[k][j][i]  = buf_r[i+j*nx+k*nx*ny]
     *
     *  where:
     *    i = 0 .. (nx-1)
     *    j = 0 .. (ny-1)
     *    k = 0 .. (nz-1)
     *  
     * =============================================================== */ 

    if (strcmp(dname,vname) == 0) {    // print one specific varaibe

      if (rank == 1) {
        nx = dim_sizes[0];
        ny = 1;
        nz = 1;
        ns = 1;
      } else if (rank == 2) {
        nx = dim_sizes[1];  // west-east
        ny = dim_sizes[0];  // south-north
        nz = 1;
        ns = 1;
      } else if (rank == 3) {
        nx = dim_sizes[2];  // west-east
        ny = dim_sizes[1];  // south-north
        nz = dim_sizes[0];  // bottom-top
        ns = 1;
      } else if (rank == 4) {
        nx = dim_sizes[3];
        ny = dim_sizes[2];
        nz = dim_sizes[1];  // soil depth
        ns = dim_sizes[0];  // number of soil type
      } else {
        continue;
      }
  
      printf("Dataset Name = %s\n",dname); 
      for (n=0;n<ns;n++) {
        nd = n*nx*ny*nz;
        for (k=0;k<nz;k++) {
          kd = k*nx*ny;
          for (j=0;j<ny;j++) {
            jd = j*nx;
            printf("%4d,%4d: ",k,j);
            for (i=0;i<nx;i++) {
              printf("%15.7f",buf_r[i+jd+kd+nd]);
            }
            printf("\n");
          }
          printf("\n");
        }
      }

    }

  } 

  /******************** Clear before retrun ****************************/

  free(buf_r);
  free(buf_i);
  free(bufc_i);
  free(buf_max);
  free(buf_min);

  istatus = SDend(sd_id);

  return ireturn;
}