/* This is part of the netCDF package. Copyright 2018 University
   Corporation for Atmospheric Research/Unidata See COPYRIGHT file for
   conditions of use. See www.unidata.ucar.edu for more info.

   Create a test file with a vlen type and vlen data for ncdump to read.

   $Id: tst_vlen_data.c,v 1.12 2009/11/15 00:17:59 dmh Exp $
*/

/* WARNING: this test leaks memory.
The leak may be in HDF5.
*/

#include <nc_tests.h>
#include <stdlib.h>
#include <netcdf.h>
#include <netcdf_aux.h>
#include "err_macros.h"

#define FILE5_NAME "tst_vlen_data.nc"
#define TYPE5_NAME "row_of_floats"
#define TYPE5_TYPE NC_FLOAT
#define DIM5_NAME "m"
#define DIM5_LEN 5
#define VAR5_NAME "ragged_array"
#define VAR5_RANK 1
#define ATT5_NAME "_FillValue"
#define ATT5_LEN  1
#define NROWS 5

/* Use reclaim_data */
#undef RECLAIM


int
main(int argc, char **argv)
{
   int ncid;
   int dimid, varid;
   nc_type typeid;
   char name_in[NC_MAX_NAME+1];
   nc_type base_typeid;
   size_t base_size_in;
   int class_in;
   float value_in;

   int i, j;

   int var_dims[VAR5_RANK];
   float **array;		/* a ragged array */
   nc_vlen_t ragged_data[DIM5_LEN];
   float missing_value = -999.0;
   nc_vlen_t missing_val;
   nc_vlen_t val_in;

   printf("\n*** Testing vlens.\n");
   printf("*** creating vlen test file %s...", FILE5_NAME);
   if (nc_create(FILE5_NAME, NC_CLOBBER | NC_NETCDF4, &ncid)) ERR;

   /* Create a vlen type. */
   if (nc_def_vlen(ncid, TYPE5_NAME, TYPE5_TYPE, &typeid)) ERR;

   /* Declare a dimension for number of rows */
   if (nc_def_dim(ncid, DIM5_NAME, DIM5_LEN, &dimid)) ERR;

   /* Declare a variable of the vlen type */
   var_dims[0] = dimid;
   if (nc_def_var(ncid, VAR5_NAME, typeid, VAR5_RANK, var_dims, &varid)) ERR;

   /* Create and write a variable attribute of the vlen type */
#ifdef RECLAIM
   /* In order to use ncaux_reclaim_data, all the interior nodes must have been alloc'd */
   missing_val.p = (float*)malloc(sizeof(missing_value));
   memcpy((void*)missing_val.p,&missing_value,sizeof(missing_value));
#else
   missing_val.p = &missing_value;
#endif
   missing_val.len = 1;
   if (nc_put_att(ncid, varid, ATT5_NAME, typeid, ATT5_LEN, (void *) &missing_val)) ERR;
   if (nc_enddef(ncid)) ERR;
#ifdef RECLAIM
   /* reclaim */
   if(ncaux_reclaim_data(ncid,typeid,&missing_val,1)) ERR;
#endif

   /* fill in pointers to data rows in preparation for writing */
   array = (float **) malloc(NROWS * sizeof(float *));
   if(array == NULL) ERR;
   for (i = 0; i < NROWS; i++) {
       int ncolumns = NROWS - i;
       array[i] = (float *) malloc((size_t)ncolumns * sizeof(float));
       if(array[i] == NULL) ERR;
       for (j = 0; j < ncolumns; j++) {
	   array[i][j] = 10.0f * (float)(i + 1) + (float)j;
       }
   }
   array[4][0] = missing_value; /* overwrite last row with missing for equality test */

   for (size_t i = 0; i < DIM5_LEN; i++) {
       ragged_data[i].p = array[i];
       ragged_data[i].len = NROWS - i;
   }

   /* Store data, writing all values of the ragged matrix in one call */
   if(nc_put_var(ncid, varid, ragged_data)) ERR;

   /* Write the file. */
   if (nc_close(ncid)) ERR;

   /* Check it out. */

   /* Reopen the file. */
   if (nc_open(FILE5_NAME, NC_NOWRITE, &ncid)) ERR;

   /* Get info with the generic inquire for user-defined types */
   if (nc_inq_user_type(ncid, typeid, name_in, &base_size_in, &base_typeid,
			NULL, &class_in)) ERR;
   if (strcmp(name_in, TYPE5_NAME) ||
       base_size_in != sizeof(nc_vlen_t) ||
       base_typeid != NC_FLOAT ||
       class_in != NC_VLEN) ERR;

   /* Get the same info with the vlen-specific inquire function */
   if (nc_inq_vlen(ncid, typeid, name_in, &base_size_in, &base_typeid)) ERR;
   if (strcmp(name_in, TYPE5_NAME) ||
       base_size_in != sizeof(nc_vlen_t) ||
       base_typeid != NC_FLOAT) ERR;

   if (nc_inq_varid(ncid, VAR5_NAME, &varid)) ERR;

   /* Read in attribute value and check it */
   if (nc_get_att(ncid, varid, ATT5_NAME, &val_in)) ERR;
   if (val_in.len != ATT5_LEN) ERR;
   value_in = *(float *)val_in.p;
   if (value_in != missing_value) ERR;
   /* Free allocated space for attribute value when finished with it */
   if (nc_free_vlen(&val_in)) ERR;

   /* Read in each row, check its length and values */
   for (size_t i = 0; i < DIM5_LEN; i++) {
       size_t index[VAR5_RANK];
       float *fvals;
       index[0] = i;
       if (nc_get_var1(ncid, varid, index, (void *) &val_in)) ERR;
       if (val_in.len != NROWS - i) ERR;
       fvals = (float *)val_in.p;
       for (j = 0; j < val_in.len; j++) {
	   if (fvals[j] != array[i][j] ) ERR;
       }
       if (nc_free_vlen(&val_in)) ERR;
   }

   /* Now read in all the rows at once, then check lengths and values */
   {
       nc_vlen_t vals_in[DIM5_LEN];
       float *fvals;
       size_t start[VAR5_RANK], count[VAR5_RANK];
       start[0] = 0;
       count[0] = NROWS;
       if (nc_get_vara(ncid, varid, start, count, vals_in)) ERR;
       for (i = 0; i < DIM5_LEN; i++) {
	   for (j = 0; j < vals_in[i].len; j++) {
	       fvals = (float *)vals_in[i].p;
	       if (fvals[j] != array[i][j] ) ERR;
	   }
	   if (nc_free_vlen(&vals_in[i])) ERR;
       }
   }


   if (nc_close(ncid)) ERR;

   /* Free space used for sample data. */
   for (i = 0; i < NROWS; i++)
      free(array[i]);
   free(array);

   SUMMARIZE_ERR;
   FINAL_RESULTS;
}
