/* This is part of the netCDF package.
   Copyright 2018 University Corporation for Atmospheric Research/Unidata
   See COPYRIGHT file for conditions of use.

   Test netcdf-4 compound type feature, even more.

   $Id: tst_compounds3.c,v 1.8 2010/05/25 13:53:04 ed Exp $
*/

#include <config.h>
#include <stdlib.h>
#include <nc_tests.h>
#include "err_macros.h"

#define FILE_NAME "tst_compounds3.nc"
#define S1_PACKED_NAME "s1_packed_compound_type_with_boring_name"
#define S1_NAME "s1_compound_type_unwhimsiclaly_named"
#define I_NAME "i_of_little_quirkiness"
#define J_NAME "j_with_no_originality"
#define DIM_NAME "intentionally_unimaginatevely_named_dimension"
#define DIM_LEN 1
#define VAR_NAME "deliberately_boring_variable"

typedef struct g1_c_t {
   float x;
   double y;
} g1_c_t;
typedef struct g2_d_t {
   g1_c_t s1;
} g2_d_t;

int
main(int argc, char **argv)
{
   printf("\n*** Testing netcdf-4 compound types even more.\n");
   printf("*** testing compound variable create from packed struct...");
   {
      int ncid, typeid, varid;
      size_t nfields;
      int dimid;
      int ndims, nvars, natts, unlimdimid;
      char name_in[NC_MAX_NAME + 1];
      size_t size;
      nc_type xtype, field_xtype;
      int dimids[] = {0};
      int field_ndims, field_sizes[NC_TESTS_MAX_DIMS];
      size_t offset;
      int i;

      struct s1_packed
      {
	 short i;
	 long long j;
      };
      /* This packing extension works with GNU compilers... */
      /* } __attribute__ ((__packed__));*/
      struct s1
      {
	 short i;
	 long long j;
      };
      struct s1_packed *data;
      struct s1 *data_in;

      if (!(data = calloc(sizeof(struct s1_packed), DIM_LEN))) ERR;
      if (!(data_in = calloc(sizeof(struct s1), DIM_LEN))) ERR;

      /* Create some phony data. */
      for (i = 0; i < DIM_LEN; i++)
      {
	 data[i].i = 100;
	 data[i].j = 1000000000000LL;
      }

      /* Create a file with a compound type. Write a little data. */
      if (nc_create(FILE_NAME, NC_NETCDF4, &ncid)) ERR;
      if (nc_def_compound(ncid, sizeof(struct s1_packed), S1_PACKED_NAME, &typeid)) ERR;
      if (nc_inq_compound(ncid, typeid, name_in, &size, &nfields)) ERR;
      if (size != sizeof(struct s1_packed) || strcmp(name_in, S1_PACKED_NAME) || nfields) ERR;
      if (nc_insert_compound(ncid, typeid, I_NAME, NC_COMPOUND_OFFSET(struct s1_packed, i),
			     NC_SHORT)) ERR;
      if (nc_insert_compound(ncid, typeid, J_NAME, NC_COMPOUND_OFFSET(struct s1_packed, j),
			     NC_INT64)) ERR;
      if (nc_def_dim(ncid, DIM_NAME, DIM_LEN, &dimid)) ERR;
      if (nc_def_var(ncid, VAR_NAME, typeid, 1, dimids, &varid)) ERR;
      if (nc_put_var(ncid, varid, data)) ERR;
      if (nc_close(ncid)) ERR;

      /* Open the file and take a peek. */
      if (nc_open(FILE_NAME, NC_WRITE, &ncid)) ERR;
      if (nc_inq(ncid, &ndims, &nvars, &natts, &unlimdimid)) ERR;
      if (ndims != 1 || nvars != 1 || natts != 0 || unlimdimid != -1) ERR;

      /* Check the var and its type. */
      if (nc_inq_var(ncid, 0, name_in, &xtype, &ndims, dimids, &natts)) ERR;
      if (strcmp(name_in, VAR_NAME) || ndims != 1 || natts != 0 || dimids[0] != 0) ERR;
      if (nc_inq_compound(ncid, xtype, name_in, &size, &nfields)) ERR;
      if (nfields != 2 || size != sizeof(struct s1) || strcmp(name_in, S1_PACKED_NAME)) ERR;
      if (nc_inq_compound_field(ncid, xtype, 0, name_in, &offset, &field_xtype, &field_ndims, field_sizes)) ERR;
      if (field_ndims) ERR;
      if (strcmp(name_in, I_NAME) || offset != NC_COMPOUND_OFFSET(struct s1, i) ||
	  (field_xtype != NC_SHORT || field_ndims != 0)) ERR;
      if (nc_inq_compound_field(ncid, xtype, 1, name_in, &offset, &field_xtype, &field_ndims,
				field_sizes)) ERR;
      if (strcmp(name_in, J_NAME) || offset != NC_COMPOUND_OFFSET(struct s1, j) ||
	  field_xtype != NC_INT64) ERR;

      /* Now check the data. */
      if (nc_get_var(ncid, varid, data_in)) ERR;
      for (i = 0; i < DIM_LEN; i++)
	 if (data[i].i != data_in[i].i || data[i].j != data_in[i].j) ERR;
      if (nc_close(ncid)) ERR;

      free(data);
      free(data_in);
   }
   SUMMARIZE_ERR;
   printf("*** testing compound attribute with Dennis...");
   {
#define GROUP1_NAME "g1"
#define GROUP2_NAME "g2"
#define TYPE1_NAME "t1"
#define TYPE2_NAME "t2"
#define ATT_NAME "a1"
#define ATT_LEN 1

      int root_grp, g1_grp, g2_grp;
      int g1_c_t_typ, g2_d_t_typ, type1id, type2id;
      char name_in[NC_MAX_NAME + 1], full_name[NC_MAX_NAME + 1];
      size_t size_in;
      g2_d_t *a1_att, *a1_att_in;
      int i;

      if (!(a1_att = calloc(sizeof(g2_d_t), ATT_LEN))) ERR;
      if (!(a1_att_in = calloc(sizeof(g2_d_t), ATT_LEN))) ERR;

      for (i = 0; i < ATT_LEN; i++)
      {
	 a1_att[i].s1.x = 13.3f;
	 a1_att[i].s1.y = 13.3;
      }
      /* Create a file with two groups, define a type in each group,
       * and write an att of that type in the root group. */
      if (nc_create(FILE_NAME, NC_NETCDF4, &root_grp)) ERR;
      if (nc_def_grp(root_grp, GROUP1_NAME, &g1_grp)) ERR;
      if (nc_def_grp(root_grp, GROUP2_NAME, &g2_grp)) ERR;
      if (nc_def_compound(g1_grp, sizeof(g1_c_t), TYPE1_NAME, &g1_c_t_typ)) ERR;
      if (nc_insert_compound(g1_grp, g1_c_t_typ, "x", NC_COMPOUND_OFFSET(g1_c_t,x), NC_FLOAT)) ERR;
      if (nc_insert_compound(g1_grp, g1_c_t_typ, "y", NC_COMPOUND_OFFSET(g1_c_t,y), NC_DOUBLE)) ERR;
      if (nc_def_compound(g2_grp, sizeof(g2_d_t), TYPE2_NAME, &g2_d_t_typ)) ERR;
      if (nc_insert_compound(g2_grp, g2_d_t_typ, "s1", NC_COMPOUND_OFFSET(g2_d_t,s1), g1_c_t_typ)) ERR;
      if (nc_put_att(root_grp, NC_GLOBAL, ATT_NAME, g2_d_t_typ, ATT_LEN, a1_att)) ERR;
      if (nc_close(root_grp)) ERR;

      /* Check the file. */
      if (nc_open(FILE_NAME, NC_WRITE, &root_grp)) ERR;

      /* Check the attribute. */
      if (nc_get_att(root_grp, NC_GLOBAL, ATT_NAME, a1_att_in)) ERR;
      if (a1_att_in[0].s1.x != a1_att[0].s1.x ||
	  a1_att_in[0].s1.y != a1_att[0].s1.y) ERR;

      /* Check the type in grp1. */
      if (nc_inq_grp_ncid(root_grp, GROUP1_NAME, &g1_grp)) ERR;
      if (nc_inq_typeid(g1_grp, TYPE1_NAME, &type1id)) ERR;
      if (nc_inq_type(g1_grp, type1id, name_in, &size_in)) ERR;
      if (strcmp(name_in, TYPE1_NAME) || size_in != sizeof(g1_c_t)) ERR;

      /* Check the type in grp2. */
      if (nc_inq_grp_ncid(root_grp, GROUP2_NAME, &g2_grp)) ERR;
      if (nc_inq_typeid(g2_grp, TYPE2_NAME, &type2id)) ERR;
      if (nc_inq_type(g2_grp, type2id, name_in, &size_in)) ERR;
      if (strcmp(name_in, TYPE2_NAME) || size_in != sizeof(g2_d_t)) ERR;

      /* This fails because it's not a fully-qualified name. */
      snprintf(full_name, sizeof(full_name), "%s/%s", GROUP2_NAME, TYPE2_NAME);
      if (nc_inq_typeid(root_grp, full_name, &type2id) != NC_EINVAL) ERR;

      /* Check the type using it's full name. */
      snprintf(full_name, sizeof(full_name), "/%s/%s", GROUP2_NAME, TYPE2_NAME);
/*      if (nc_inq_typeid(root_grp, full_name, &type2id)) ERR;       */
/*       if (nc_inq_type(g2_grp, type2id, name_in, &size_in)) ERR; */
/*       if (strcmp(name_in, TYPE2_NAME) || size_in != sizeof(g2_d_t)) ERR; */

      /* Check that a type in grp1 can be identified in grp2 */
      type1id = -1;
      if (nc_inq_typeid(g2_grp, TYPE1_NAME, &type1id)) ERR;
      if (nc_inq_type(g2_grp, type1id, name_in, &size_in)) ERR;
      if (strcmp(name_in, TYPE1_NAME) || size_in != sizeof(g1_c_t)) ERR;

      /* We are done! */
      if (nc_close(root_grp)) ERR;
      free(a1_att);
      free(a1_att_in);
   }
   SUMMARIZE_ERR;
   FINAL_RESULTS;
}
