/*********************************************************************
 *   Copyright 1993, UCAR/Unidata
 *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
 *   $Id$
 *********************************************************************/

#include <stdio.h>
#include <stdlib.h>

#include "h4config.h"
#ifdef H4_HAVE_NETCDF
#include "netcdf.h"
#else
#include "hdf4_netcdf.h"
#endif

#include "testcdf.h"		/* defines in-memory test cdf structure */
#include "add.h"		/* functions to update in-memory netcdf */
#include "val.h"
#include "error.h"
#include "tests.h"
#include "alloc.h"
#include "emalloc.h"
#ifdef HDF
#include "hdf.h"
#endif

#define max(A, B)	((A) > (B) ? (A) : (B))

/* 
 * For every variable in open netcdf, puts and gets three hypercubes 
 * of data of the appropriate type, comparing values from get to 
 * values put to check that both ncvarputg and ncvargetg worked.  The 
 * three hypercubes are
 *    - a large hypercube from (0, 0, ...) to the far corner (diagonally 
 *      opposite (0, 0, ...),
 *    - a size 1 hypercube from the far corner with edge lengths of 1 
 *      in every direction, and
 *    - a hypercube starting about 1/3 of the way along the diagonal
 *      from (0,0,...) extending 1/3 of the way in every direction 
 *      toward the far corner.
 */

int
test_varputgetg(cdfid)
     int cdfid;			/* handle of netcdf open and in data mode */
{
    int nerrs = 0;
    static char pname[] = "test_varputgetg";
    int id, ie, iv;		/* loop indices */
    int ne = 3;			/* number of test hypercubes for each var */
    struct cdfhc {		/* a hypercube with generic values */
	long cor[H4_MAX_VAR_DIMS];	/* netcdf coordinates for lower corner */
	long edg[H4_MAX_VAR_DIMS];	/* netcdf edge lengths to upper corner */
	void *vals;		/* pointer to block of values */
    } hc[3], tmp;		/* test hypercubes */
    long nel[3];		/* number of elements in hypercube */
    long strides[H4_MAX_VAR_DIMS];	/* external strides */
    long basis[H4_MAX_VAR_DIMS];	/* internal array, element-access 
				   basis vector */

    for (iv = 0; iv < test.nvars; iv++)	{ /* for each var in netcdf */
	long	tmpbasis;

	for (ie = 0; ie < ne; ie++)
	  nel[ie] = 1;		/* to compute space for hypercube values */

	/*
	 * The following macro returns the size of a dimension for a
	 * variable with a maximum  dimension size of 5 for the record
	 * dimension.
	 */
#	define EXTNPTS(varid, idim)	\
	    (test.dims[test.vars[varid].dims[id]].size == NC_UNLIMITED \
		? 5 \
		: test.dims[test.vars[varid].dims[id]].size)
#	define STRIDE(idim)		(idim + 2)
#	define INTNPTS(extnpts, idim)	(1 + (extnpts - 1) / STRIDE(idim))


	for (id = 0; id < test.vars[iv].ndims; id++) { /* set cubes */
	    strides[id]	= STRIDE(id);

	    /* start at "lower-left" corner, do whole variable */
	    hc[0].cor[id] = 0;
	    hc[0].edg[id] = INTNPTS(EXTNPTS(iv, id), id);
	    nel[0] *= hc[0].edg[id];

	    /* start at "upper-right" corner, do one point */
	    hc[1].cor[id] = EXTNPTS(iv, id) - 1;
	    hc[1].edg[id] = 1;
	    nel[1] *= hc[1].edg[id];

	    /* start about 1/3 way along diagonal, do 1/3 in each direction */
	    hc[2].cor[id] = EXTNPTS(iv, id)/3;
	    hc[2].edg[id] = INTNPTS(max(EXTNPTS(iv, id)/3, 1), id);
	    nel[2] *= hc[2].edg[id];
	}
	for (ie = 0; ie < ne; ie++) { /* for each of ne points */
	    int nelms = nel[ie]*nctypelen(test.vars[iv].type) + 8;
	    /* allocate space for the cube of values */
	    hc[ie].vals = emalloc(nelms);
	    tmp.vals = emalloc(nelms);

	    /* fill allocated space with different values of right type */
	    val_fill(test.vars[iv].type, nel[ie], hc[ie].vals);

	    /*
	     * Set internal-array element-access basis vector to be negative 
	     * of natural storage so as to access the elements of the array
	     * backwards.
	     */
	    tmpbasis	= nctypelen(test.vars[iv].type);
	    for (id = test.vars[iv].ndims-1; id >= 0; --id) {
		basis[id]	 = -tmpbasis;
		tmpbasis	*= hc[ie].edg[id];
	    }

	    if(ncvarputg (cdfid, iv, hc[ie].cor, hc[ie].edg, 
			  strides, basis, 
			  (char*)hc[ie].vals+(nel[ie]-1)*
			      nctypelen(test.vars[iv].type))
	       == -1) {
		error("%s: ncvarputg failed for point %d, variable %s",
		      pname, ie, test.vars[iv].name);
		nerrs++;
		errvar(&test, &test.vars[iv]);
		(void)fprintf(stderr,"  corner = (");
		for (id = 0 ; id < test.vars[iv].ndims; id++)
		  (void)fprintf(stderr,"%d%s",(int)hc[ie].cor[id],
				(id < test.vars[iv].ndims-1) ? ", " : "");
		(void)fprintf(stderr,")\n");
		(void)fprintf(stderr,"  edge = (");
		for (id = 0 ; id < test.vars[iv].ndims; id++)
		  (void)fprintf(stderr,"%d%s",(int)hc[ie].edg[id],
				(id < test.vars[iv].ndims-1) ? ", " : "");
		(void)fprintf(stderr,")\n");
		(void)fprintf(stderr,"  external strides = (");
		for (id = 0 ; id < test.vars[iv].ndims; id++)
		  (void)fprintf(stderr,"%d%s",(int)strides[id],
				(id < test.vars[iv].ndims-1) ? ", " : "");
		(void)fprintf(stderr,")\n");
		(void)fprintf(stderr,"  internal basis vector = (");
		for (id = 0 ; id < test.vars[iv].ndims; id++)
		  (void)fprintf(stderr,"%d%s",(int)basis[id],
				(id < test.vars[iv].ndims-1) ? ", " : "");
		(void)fprintf(stderr,")\n");
	    } else {
		long	dsize[H4_MAX_VAR_DIMS];

		for (id = 0; id < test.vars[iv].ndims; id++)
		    dsize[id]	= EXTNPTS(iv, id);
		add_data(&test, iv, hc[ie].cor, dsize);
						    /* keep test in sync */
		if(ncvargetg (cdfid, iv, hc[ie].cor, hc[ie].edg, 
			      strides, basis,
			      (char*)tmp.vals+(nel[ie]-1)*
				  nctypelen(test.vars[iv].type))
		   == -1) {
		    error("%s: ncvargetg failed for point %d, variable %s",
			  pname, ie, test.vars[iv].name);
		    nerrs++;
		}
		else {
		    if (val_cmp(test.vars[iv].type, nel[ie],
				hc[ie].vals, tmp.vals) != 0) {
			error("%s: bad values returned from ncvargetg",
			      pname);
			nerrs++;
			errvar(&test, &test.vars[iv]); /* describe var */
		    }
		}
	    }

	    Free ((char *) hc[ie].vals);
	    Free ((char *) tmp.vals);
	}
    }
    return nerrs;
}
