#include "cdf.h"
#include "cdffits.h"

/******************************************************************************
* QuitCDF.
******************************************************************************/

void QuitCDF (char *where, CDFstatus status) {

  char text[CDF_STATUSTEXT_LEN+1];
  printf ("ERROR at %s...\n", where);
  if (status < CDF_OK) {
    CDFlib (SELECT_, CDF_STATUS_, status,
            GET_, STATUS_TEXT_, text,
            NULL_);
    printf ("%s\n", text);
  }
  CDFlib (CLOSE_, CDF_,
          NULL_);
  exit(1);
}

/******************************************************************************
* QuitFITS.
******************************************************************************/

void QuitFITS (char *where, char *text) {
  printf ("ERROR at %s...\n", where);
  printf ("FITS: %s\n", text);
  exit(1);
}

/******************************************************************************
* QuitX.
******************************************************************************/

void QuitX (char *where, char *msg) {

  printf ("ERROR at %s...\n", where);
  printf ("      msg: %s\n", msg);
  exit(1);
}

/**************************************************************************
 * Open a CDF.
 **************************************************************************/
CDFid OpenCDF(char *CDFpath) {

    CDFstatus status;
    CDFid id;

    status = CDFlib (OPEN_, CDF_, CDFpath, &id,
                     SELECT_, CDF_READONLY_MODE_, READONLYon,
                              CDF_zMODE_, zMODEon2,
                     NULL_);

    if (status < CDF_OK) QuitCDF ("10.0", status);

    return id;

}

/**************************************************************************
 * Close a CDF.
 **************************************************************************/
void CloseCDF(CDFid id) {

    CDFstatus status;

    status = CDFlib (SELECT_, CDF_, id,
                     CLOSE_, CDF_, 
                     NULL_);

    if (status < CDF_OK) QuitX ("60.0", "Error... at closing the CDF");
    
    return;
}

/**************************************************************************
 * Acquire certain infomation from the CDF, e.g., majority, number of total 
 * attributes, number of global attributes and variable attributes, and
 * the maximum record number among all variables.
 **************************************************************************/
void GetCDFInfo (CDFid id, long *majority, long *numAttrs, long *numGattrs, 
                 long *numVattrs, long *numZvars, long *maxRecNum) {

    CDFstatus status;

    status = CDFlib (SELECT_, CDF_, id,
                     GET_, CDF_MAJORITY_, majority,
                           CDF_NUMATTRS_, numAttrs,
                           CDF_NUMgATTRS_, numGattrs,
                           CDF_NUMvATTRS_, numVattrs,
                           CDF_NUMzVARS_, numZvars,
                           zVARs_MAXREC_, maxRecNum,
                     NULL_);

    if (status < CDF_OK) QuitCDF ("15.0", status);
    
    return;
}

/****************************************************************************
 * Acquire a global attribute's entry info, e.g., their entry numbers,  
 * data types, number of elements and data contents.
 ****************************************************************************/
void GetGlobalAttributeEntries (CDFid id, long gAttrNum, 
                                long numEntries, long *entryNums, 
                                long *entryDataType, long *entryNumElems, 
                                void **entries) {
  int jj;
  long kk, status;
  size_t bufferSize;
  long maxEntry;
  long dataType, numElems;
 
  status = CDFlib (SELECT_, CDF_, id,
                            ATTR_, gAttrNum,
                   NULL_);

  kk = 0;

  /**********************************************************************
  * Acquire info for each entry.
  **********************************************************************/
  for (jj = 0; jj < numEntries; jj++) {
    while (TRUE) { /* break it until an entry is found */
      status = CDFlib (SELECT_, gENTRY_, (long) kk,
                       GET_, gENTRY_DATATYPE_, &dataType,
                             gENTRY_NUMELEMS_, &numElems,
                       NULL_);

      if (status == CDF_OK) {
        entryNums[jj] = kk;
	entryDataType[jj] = dataType;
	entryNumElems[jj] = numElems;
        if (dataType == CDF_CHAR || dataType == CDF_UCHAR)
          bufferSize = (size_t) (numElems+1);
        else
          bufferSize = (size_t)CDFelemSize(dataType)*numElems;

        entries[jj] = (void *) malloc (bufferSize);
        status = CDFlib (GET_, gENTRY_DATA_, (void *) entries[jj],
                         NULL_);
	kk++;
	break;
      }
      kk++;
    }
  }
  return;
}

/****************************************************************************
 * Acquire a global attribute's entry data.
 ****************************************************************************/
void GetGlobalAttributeEntryData (CDFid id, long gAttrNum, long entryNum, 
                                  void *entry) { 
  CDFstatus status;
 
  status = CDFlib (SELECT_, CDF_, id,
                            ATTR_, gAttrNum,
                            gENTRY_, entryNum,
                   GET_, gENTRY_DATA_, entry,
                   NULL_);
  if (status < CDF_OK) QuitCDF ("20.0", status);
  
  return;
}

/****************************************************************************
 * Acquire a global attribute's entry info, e.g., data type, number of 
 * elements, if it exists.
 ****************************************************************************/
CDFstatus GetGlobalAttributeEntryInfo (CDFid id, long gAttrNum, long entryNum, 
                                       long *dataType, long *numElems) {
  CDFstatus status;
 
  status = CDFlib (SELECT_, CDF_, id,
                            ATTR_, gAttrNum,
                            gENTRY_, entryNum,
                   GET_, gENTRY_DATATYPE_, dataType,
                         gENTRY_NUMELEMS_, numElems,
                   NULL_);

  return status;

}

/****************************************************************************
 * Acquire a global attribute's info., e.g., its name, number of 
 * entries, and the maximum entry number.
 ****************************************************************************/
void GetGlobalAttributeInfo (CDFid id, long gAttrNum, char *attr_name, 
                             long *numEntries, long *maxEntry) { 
  CDFstatus status;
 
  status = CDFlib (SELECT_, CDF_, id,
                            ATTR_, gAttrNum,
                   GET_,    ATTR_NAME_, attr_name,
                            ATTR_MAXgENTRY_, maxEntry,
                            ATTR_NUMgENTRIES_, numEntries,
                   NULL_);

  if (status < CDF_OK) QuitCDF("25.0", status);
  
  return;
}

/****************************************************************************
 * Acquire global attributes's numbers from the attribute list.
 ****************************************************************************/
void GetGlobalAttributeNumbers (CDFid id, long numAttrs, long *gAttrNum) { 

   int ii, kk;
   long scope, status;

   kk = 0;
   for (ii = 0; ii < numAttrs; ii++) {
     status = CDFlib (SELECT_, CDF_, id,
                               ATTR_, (long) ii,
                      GET_, ATTR_SCOPE_, &scope,
                      NULL_);

     if (status < CDF_OK) QuitCDF ("25.5", status);

     if (scope == GLOBAL_SCOPE) {
       gAttrNum[kk] = ii;
       kk++;
     }
   }
   return;
}

/****************************************************************************
 * Acquire a variable's pad value, if it exists.
 ****************************************************************************/
CDFstatus GetVarPadValue (CDFid id, long varNum, void *value) {
  CDFstatus status;
 
  status = CDFlib (SELECT_, CDF_, id,
                            zVAR_, varNum,
                   CONFIRM_, zVAR_PADVALUE_,
                   NULL_);

  if (status == NO_PADVALUE_SPECIFIED) 
    return status;
  else
    status = CDFlib (GET_, zVAR_PADVALUE_, value,
                     NULL_);
  return status;
}

/****************************************************************************
 * Acquire a variable attribute's entry data.
 ****************************************************************************/
void GetVarAttributeEntryData (CDFid id, long zAttrNum, long entryNum,
                               void *entry) {
  CDFstatus status;

  status = CDFlib (SELECT_, CDF_, id,
                            ATTR_, zAttrNum,
                            zENTRY_, entryNum,
                   GET_, zENTRY_DATA_, entry,
                   NULL_);

  if (status < CDF_OK) QuitCDF ("30.0", status);
  
  return;
}

/****************************************************************************
 * Acquire a variable attribute's entry info, e.g., data type, number of 
 * elements, if it exists.
 ****************************************************************************/
CDFstatus GetVarAttributeEntryInfo (CDFid id, long zAttrNum, long entryNum, 
                                    long *dataType, long *numElems) {
  CDFstatus status;
 
  status = CDFlib (SELECT_, CDF_, id,
                            ATTR_, zAttrNum,
                            zENTRY_, entryNum,
                   GET_, zENTRY_DATATYPE_, dataType,
                         zENTRY_NUMELEMS_, numElems,
                   NULL_);

  return status;

}

/****************************************************************************
 * Acquire variable attributes's numbers and names from the attribute list.
 ***************************************************************************/
void GetVarAttributes (CDFid id, long numAttrs, long *vAttrNum,
                       char **attrNames) {

   int ii, kk;
   long scope, status;
   char name[DU_MAX_PATH_LEN+1];

   kk = 0;
   for (ii = 0; ii < numAttrs; ii++) {
     status = CDFlib (SELECT_, CDF_, id,
                               ATTR_, (long) ii,
                      GET_, ATTR_SCOPE_, &scope,
                            ATTR_NAME_, name,
                      NULL_);

     if (status < CDF_OK) QuitCDF ("40.0", status);

     if (scope == VARIABLE_SCOPE) {
       vAttrNum[kk] = ii;
       strcpyX((char *)attrNames[kk], name, 0);
       kk++;
     }
   }
   return;
}

/**************************************************************************
 * Acquire certain information from a variable.
 **************************************************************************/
void GetVarInfo (CDFid id, long varNum, char *var_name, long *dataType, 
                 long *numElems, long *numDims, long *dimSizes,  
                 long *recVary, long *maxRec) {

    CDFstatus status;

    status = CDFlib (SELECT_, CDF_, id,
                              zVAR_, varNum,
                     GET_, zVAR_NAME_, var_name,
                           zVAR_DATATYPE_, dataType,
                           zVAR_NUMELEMS_, numElems,
                           zVAR_NUMDIMS_, numDims,
                           zVAR_DIMSIZES_, dimSizes,
                           zVAR_RECVARY_, recVary,
                           zVAR_MAXREC_, maxRec,
                     NULL_);

    if (status < CDF_OK) QuitCDF ("45.0", status);
    
    return;
}

/**************************************************************************
 * Acquire the variable names and their maximum record numbers in a CDF.
 * The HDU number and its column where a variable will be written is also
 * determined.  It returns the number of different record numbers.
 **************************************************************************/
int GetVarsRecordNumbers (CDFid id, char **varNames, long *numRecords,
                          long *hduGroup, int *varColumn) {

    long numZvars, maxRec;
    char varName[127];
    CDFstatus status;
    int i, k, group;
    int *tmp1, *tmp2;
    Logical found;
    
    status = CDFlib (SELECT_, CDF_, id,
                     GET_, CDF_NUMzVARS_, &numZvars,
                     NULL_);

    if (status < CDF_OK) QuitCDF ("45.2", status);

    if (numRecords == NULL) 
      numRecords = malloc (sizeof(long) * numZvars);
    if (varNames == NULL)
      varNames = malloc (sizeof(char *) * numZvars);

    for (i = 0; i < numZvars; i++) {
      status = CDFlib (SELECT_, CDF_, id,
                                zVAR_, (long) i,
                       GET_, zVAR_MAXREC_, &maxRec,
                             zVAR_NAME_, varName,
                       NULL_);

      if (status < CDF_OK) QuitCDF ("45.5", status);

      varNames[i] = (char *) strdup(varName);
      numRecords[i] = maxRec + 1;

    }

    tmp1 = malloc(sizeof(int) * numZvars * 2);
    tmp2 = malloc(sizeof(int) * numZvars);

    tmp1[0] = numRecords[0];   /* rec num */
    tmp1[1] = 1;               /* HDU # */
    tmp2[0] = 1;               /* coulum # in a HDU */
    varColumn[0] = 1;
    group = 1;
    hduGroup[0] = 1;

    for (i = 1; i < numZvars; i++) {
      found = FALSE;
      for (k = 0; k < group; k++) {
        if (numRecords[i] == tmp1[2*k]) {
          found = TRUE;
          hduGroup[i] = tmp1[2*k+1]; 
          tmp2[k] = tmp2[k] + 1;
          varColumn[i] = tmp2[k];
          break;
        }
      }
      if (!found) {
        tmp1[2*group] = numRecords[i];
        tmp1[2*group+1] = group + 1;
        hduGroup[i] = group + 1;
        varColumn[i] = 1;
        tmp2[group] = 1;
        group++;
      } 
    }

    free (tmp1);
    free (tmp2);
   
    return group;
}

/****************************************************************************
 * Read variable record values. 
 ****************************************************************************/
void GetVarRecordData(CDFid id, long varNum, long startRec, long numRec, 
                      long numDims, long *dimSizes, 
                      void *outData) {

   int jk;
   CDFstatus status;
   long dimIndices[CDF_MAX_DIMS], dimIntervals[CDF_MAX_DIMS];
    
   if (numDims > 0) {
     for (jk = 0; jk < numDims; jk++) {
       dimIndices[jk] = 0L;
       dimIntervals[jk] = 1L;
     }
   } else {
     dimIndices[0] = 0L;
     dimSizes[0] = 1L;
     dimIntervals[0] = 1L;
   }

   /****************************************************************************
    * Acquire record values. 
    ****************************************************************************/
   status = CDFlib (SELECT_, CDF_, id,
                             zVAR_, varNum,
                             zVAR_RECNUMBER_, startRec,
                             zVAR_RECCOUNT_, numRec,
                             zVAR_RECINTERVAL_, 1L,
                             zVAR_DIMINDICES_, dimIndices,
                             zVAR_DIMCOUNTS_, dimSizes,
                             zVAR_DIMINTERVALS_, dimIntervals,
                    GET_, zVAR_HYPERDATA_, outData,
                    NULL_);

   if (status < CDF_OK) QuitCDF("50.0", status);

   return;     

}

