/******************************************************************************
*
*  NSSDC/CDF                                           CDF `close' operations.
*
*  Version 1.5a, 21-Feb-97, Hughes STX.
*
*  Modification history:
*
*   V1.0  20-May-92, J Love     Original version (was part of `cdflib.c').
*   V1.1  21-Aug-92, J Love     CDF V2.3 (shareable/NeXT/zVar).
*   V1.2   7-Dec-93, J Love     CDF V2.4.  Added zMode.
*   V1.3  15-Nov-94, J Love     CDF V2.5.
*   V1.3a 24-Feb-95, J Love	Solaris 2.3 IDL i/f.
*   V1.4  20-Jul-95, J Love	CDFexport-related changes.
*   V1.5   8-Aug-96, J Love	CDF V2.6.
*   V1.4a 21-Feb-97, J Love	Removed RICE.
*   V2.0  08-Apr-04, M Liu      Replaced VSTREAM.STATS with VSTREAM_STATS.
*                               Removed call to LocateCurrentVar when r/zVAR_
*                               is closed as it does more than needed.
*
******************************************************************************/

#include "cdflib.h"

/******************************************************************************
* CDFclo.
******************************************************************************/

STATICforIDL CDFstatus CDFclo (Va, Cur)
struct VAstruct *Va;
struct CurStruct *Cur;
{
  CDFstatus pStatus = CDF_OK;
  switch (Va->item) {
    /**************************************************************************
    * CDF_
    *   Close a CDF with or without statistics for the dotCDF file.
    **************************************************************************/
    case CDF_:
    case CDFwithSTATS_: {
      struct CDFstruct *CDF; vSTATS vStats;
      Logical statistics = (Va->item == CDFwithSTATS_);
      vSTATS *vStatsDotP = BOO(statistics,va_arg(Va->ap,vSTATS *),NULL);
      vSTATS *vStatsStageP = BOO(statistics,va_arg(Va->ap,vSTATS *),NULL);
      vSTATS *vStatsCompressP = BOO(statistics,va_arg(Va->ap,vSTATS *),NULL);
      /************************************************************************
      * Initialize the Vstream statistics.
      ************************************************************************/
      AddTOvStats (vStatsDotP, NULL);
      AddTOvStats (vStatsStageP, NULL);
      AddTOvStats (vStatsCompressP, NULL);
      /************************************************************************
      * Validate the current CDF.  Don't use `SelectCDF' here because we don't
      * want to return NO_MORE_ACCESS if that is the case.
      ************************************************************************/
      if (Cur->cdf == NULL)
	return NO_CDF_SELECTED;
      else
	CDF = Cur->cdf;
      /************************************************************************
      * If this is a CDF for which access was aborted, simply free the CDF's
      * memory.
      ************************************************************************/
      if (CDF->magic == ABORTEDid_MAGIC_NUMBER) {
	KillAbortedCDF (CDF, Cur);
	break;
      }
      /************************************************************************
      * Get the Vstream statistics for the dotCDF file(s) so far.
      ************************************************************************/
      AddTOvStats (vStatsDotP, &CDF->dotCDFvStats);
      AddTOvStats (vStatsDotP, &CDF->uDotCDFvStats);
      /************************************************************************
      * If the CDF is open read/write, update the dotCDF file.
      ************************************************************************/
      if (CDF->status == READ_WRITE) {
	if (!sX(UpdateDotCDF(CDF),&pStatus)) {
	  AbortAccess (CDF, noUPDATE, noDELETE);
	  KillAbortedCDF (CDF, Cur);
	  return pStatus;
	}
      }
      /************************************************************************
      * If the CDF is compressed...
      ************************************************************************/
      if (CDF->uDotFp != NULL) {
	/**********************************************************************
	* If the CDF is open read/write... The uncompressed dotCDF file will
        * be flushed and the compressed dotCDF will be deleted and recreated
        * from the uncompressed dotCDF file.
	**********************************************************************/
	if (CDF->status == READ_WRITE) {
	  char pathName[DU_MAX_PATH_LEN+1];
	  Int32 CPRoffset; struct CPRstruct CPR;
	  /********************************************************************
	  * First flush the uncompressed dotCDF file.
	  ********************************************************************/
	  if (!FLUSHv(CDF->uDotFp)) {
	    AbortAccess (CDF, noUPDATE, noDELETE);
	    KillAbortedCDF (CDF, Cur);
	    return CDF_WRITE_ERROR;
	  }
	  /********************************************************************
	  * Read the compression parameters from the compressed dotCDF file.
	  ********************************************************************/
	  if (!sX(ReadCCR(CDF->dotFp,V2_CCR_OFFSET,
			  CCR_CPROFFSET,&CPRoffset,
			  CCR_NULL),&pStatus)) {
	    AbortAccess (CDF, noUPDATE, noDELETE);
	    KillAbortedCDF (CDF, Cur);
	    return pStatus;
	  }
	  if (!sX(ReadCPR(CDF->dotFp,CPRoffset,
			  CPR_RECORD,&CPR,
			  CPR_NULL),&pStatus)) {
	    AbortAccess (CDF, noUPDATE, noDELETE);
	    KillAbortedCDF (CDF, Cur);
	    return pStatus;
	  }
	  /********************************************************************
	  * Delete the compressed dotCDF file.
	  ********************************************************************/
	  if (!DELETEv(CDF->dotFp,&vStats)) {
	    CDF->dotFp = NULL;
	    AbortAccess (CDF, noUPDATE, noDELETE);
	    KillAbortedCDF (CDF, Cur);
	    return CDF_CLOSE_ERROR;
	  }
	  CDF->dotFp = NULL;
	  AddTOvStats (vStatsDotP, &vStats);
#if defined(DEBUG)
	  DisplayVs (getenv("VSTREAM_STATS"), "DotCDF..", &vStats);
#endif
	  /********************************************************************
	  * Recreate the compressed dotCDF file.
	  ********************************************************************/
	  BuildFilePath (CDFt, CDF->CDFname, CDF->no_append,
			 CDF->upper_case_ext, CDF->version_numbers,
			 INT32_ZERO, pathName);
	  CDF->dotFp = V_open (pathName, WRITE_PLUS_a_mode);
	  if (CDF->dotFp == NULL) {
	    AbortAccess (CDF, noUPDATE, noDELETE);
	    KillAbortedCDF (CDF, Cur);
	    return CDF_CREATE_ERROR;
	  }
	  /********************************************************************
	  * Write the compressed dotCDF file.
	  ********************************************************************/
	  if (!sX(WriteCompressedCDF(CDF,&CPR,notEMPTY),&pStatus)) {
	    AbortAccess (CDF, noUPDATE, noDELETE);
	    KillAbortedCDF (CDF, Cur);
	    return pStatus;
	  }
	  /********************************************************************
	  * Close the compressed dotCDF file.
	  ********************************************************************/
	  if (!CLOSEv(CDF->dotFp,CDF,&vStats)) {
	    CDF->dotFp = NULL;
	    AbortAccess (CDF, noUPDATE, noDELETE);
	    KillAbortedCDF (CDF, Cur);
	    return CDF_CLOSE_ERROR;
	  }
	  CDF->dotFp = NULL;
	  AddTOvStats (vStatsDotP, &vStats);
#if defined(DEBUG)
	  DisplayVs (getenv("VSTREAM_STATS"), "DotCDF..", &vStats);
#endif
	  /********************************************************************
	  * Delete the uncompressed dotCDF file.
	  ********************************************************************/
	  if (!DELETEv(CDF->uDotFp,&vStats)) {
	    CDF->uDotFp = NULL;
	    AbortAccess (CDF, noUPDATE, noDELETE);
	    KillAbortedCDF (CDF, Cur);
	    return CDF_CLOSE_ERROR;
	  }
	  CDF->uDotFp = NULL;
	  AddTOvStats (vStatsDotP, &vStats);
#if defined(DEBUG)
	  DisplayVs (getenv("VSTREAM_STATS"), "uDotCDF.", &vStats);
#endif
	}
	else {
	  /********************************************************************
	  * The CDF is open read/only.  First close the compressed dotCDF file
	  * (which has not been changed).
	  ********************************************************************/
	  if (!CLOSEv(CDF->dotFp,CDF,&vStats)) {
	    CDF->dotFp = NULL;
	    AbortAccess (CDF, noUPDATE, noDELETE);
	    KillAbortedCDF (CDF, Cur);
	    return CDF_CLOSE_ERROR;
	  }
	  CDF->dotFp = NULL;
	  AddTOvStats (vStatsDotP, &vStats);
#if defined(DEBUG)
	  DisplayVs (getenv("VSTREAM_STATS"), "DotCDF..", &vStats);
#endif
	  /********************************************************************
	  * Delete the uncompressed dotCDF file.
	  ********************************************************************/
	  if (!DELETEv(CDF->uDotFp,&vStats)) {
	    CDF->uDotFp = NULL;
	    AbortAccess (CDF, noUPDATE, noDELETE);
	    KillAbortedCDF (CDF, Cur);
	    return CDF_CLOSE_ERROR;
	  }
	  CDF->uDotFp = NULL;
	  AddTOvStats (vStatsDotP, &vStats);
#if defined(DEBUG)
	  DisplayVs (getenv("VSTREAM_STATS"), "uDotCDF.", &vStats);
#endif
	}
      }
      else {
        /**********************************************************************
	* Not compressed - first close the dotCDF file.
	**********************************************************************/
	if (!CLOSEv(CDF->dotFp,CDF,&vStats)) {
	  CDF->dotFp = NULL;
	  AbortAccess (CDF, noUPDATE, noDELETE);
	  KillAbortedCDF (CDF, Cur);
	  return CDF_CLOSE_ERROR;
	}
	CDF->dotFp = NULL;
	AddTOvStats (vStatsDotP, &vStats);
#if defined(DEBUG)
	DisplayVs (getenv("VSTREAM_STATS"), "DotCDF..", &vStats);
#endif
	/**********************************************************************
	* Close the variable files (if a multi-file CDF).
	**********************************************************************/
	if (!sX(CloseVarFiles(CDF),&pStatus)) {
	  AbortAccess (CDF, noUPDATE, noDELETE);
	  KillAbortedCDF (CDF, Cur);
	  return pStatus;
	}
      }
      /************************************************************************
      * Delete staging scratch file.
      ************************************************************************/
      if (CDF->stage.fp != NULL) {
	if (!DELETEv(CDF->stage.fp,&vStats)) {
	  CDF->stage.fp = NULL;
	  AbortAccess (CDF, noUPDATE, noDELETE);
	  KillAbortedCDF (CDF, Cur);
	  return SCRATCH_DELETE_ERROR;
	}
	CDF->stage.fp = NULL;
	AddTOvStats (vStatsStageP, &vStats);
#if defined(DEBUG)
	DisplayVs (getenv("VSTREAM_STATS"), "Stage...", &vStats);
#endif
      }
      /************************************************************************
      * Delete compression scratch file.
      ************************************************************************/
      if (CDF->compressFp != NULL) {
	if (!DELETEv(CDF->compressFp,&vStats)) {
	  CDF->compressFp = NULL;
	  AbortAccess (CDF, noUPDATE, noDELETE);
	  KillAbortedCDF (CDF, Cur);
	  return SCRATCH_DELETE_ERROR;
	}
	CDF->compressFp = NULL;
	AddTOvStats (vStatsCompressP, &vStats);
#if defined(DEBUG)
	DisplayVs (getenv("VSTREAM_STATS"), "Compress", &vStats);
#endif
      }
      /************************************************************************
      * Free memory used and clear current CDF.
      ************************************************************************/
      FreeCDFid (CDF, FALSE);
      Cur->cdf = NULL;
      break;
    }
    /**************************************************************************
    * rVAR_/zVAR_
    *    Close the current r/zVariable file.
    **************************************************************************/
    case rVAR_:
    case zVAR_: {
      Logical zOp = (Va->item == zVAR_);
      struct CDFstruct *CDF; struct VarStruct *Var;
      SelectCDF (Cur->cdf, CDF)
      if (BADzOP(CDF,!zOp)) return ILLEGAL_IN_zMODE;
      if (!CURRENTvarSELECTED(CDF,zOp)) return NO_VAR_SELECTED;

      if (zModeON(CDF)) {
        if (CDF->CURzVarNum < CDF->NrVars)
          Var = CDF->rVars[(int)CDF->CURzVarNum];
        else
          Var = CDF->zVars[(int)(CDF->CURzVarNum - CDF->NrVars)];
      } else
        Var = BOO(zOp,CDF->zVars[(int)CDF->CURzVarNum],
                  CDF->rVars[(int)CDF->CURrVarNum]);

      if (CDF->singleFile) {
	sX (SINGLE_FILE_FORMAT, &pStatus);
	break;
      }
      if (Var != NULL) {
	if (Var->fp != NULL) {
	  if (!CLOSEv(Var->fp,NULL,NULL)) {
	    Var->fp = NULL;
	    AbortAccess (CDF, UPDATE, noDELETE);
	    return VAR_CLOSE_ERROR;
	  }
	  Var->fp = NULL;
	}
	else {
	  if (!sX(VAR_ALREADY_CLOSED,&pStatus)) return pStatus;
	}
      }
      else {
	if (!sX(VAR_ALREADY_CLOSED,&pStatus)) return pStatus;
      }
      break;
    }
    /**************************************************************************
    * Unknown item, must be the next function.
    **************************************************************************/
    default: {
      Va->fnc = Va->item;
      break;
    }
  }
  return pStatus;
}
