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

   Test fix of bug involving creation of a file with PnetCDF APIs,
   then opening and modifying the file with netcdf.

   Author: Wei-keng Liao.
*/

#include <nc_tests.h>
#include "err_macros.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mpi.h>
#include <netcdf.h>
#include <netcdf_par.h>
#include <assert.h>

#define NVARS 6
#define NX    5
#define FILENAME "tst_pnetcdf.nc"

#define CHK_ERR(e) { \
    if (e != NC_NOERR) { \
        printf("Error at %s:%d : %s\n", __FILE__,__LINE__, nc_strerror(e)); \
        goto fn_exit; \
    } \
}

#define EXP_ERR(e, exp) { \
    if (e != exp) { \
        printf("Error at %s:%d : expect "#exp" but got %d\n", __FILE__,__LINE__, e); \
    } \
}


int main(int argc, char* argv[])
{
    int i, j, rank, nprocs, ncid, cmode, varid[NVARS], dimid[2], *buf=NULL;
    int st, nerrs=0;
    char str[32];
    size_t start[2], count[2];
    MPI_Comm comm=MPI_COMM_SELF;
    MPI_Info info=MPI_INFO_NULL;
    char file_name[NC_MAX_NAME + 1];
    
    printf("\n*** Testing bug fix with changing PnetCDF variable offsets...");

    MPI_Init(&argc,&argv);
    MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    if (nprocs > 1 && rank == 0)
        printf("This test program is intended to run on ONE process\n");
    if (rank > 0) goto fn_exit;

    /* first, use PnetCDF to create a file with default header/variable alignment */
#ifdef DISABLE_PNETCDF_ALIGNMENT
    MPI_Info_create(&info);
    MPI_Info_set(info, "nc_header_align_size", "1");
    MPI_Info_set(info, "nc_var_align_size",    "1");
#endif

    cmode = NC_CLOBBER;
    snprintf(file_name, sizeof(file_name), "%s/%s", TEMP_LARGE, FILENAME);
    st = nc_create_par(file_name, cmode, comm, info, &ncid);

#ifdef USE_PNETCDF
    CHK_ERR(st)
#else
    EXP_ERR(st, NC_ENOTBUILT)
    goto fn_exit;
#endif

    /* define dimension */
    st = nc_def_dim(ncid, "Y", NC_UNLIMITED, &dimid[0]); CHK_ERR(st)
    st = nc_def_dim(ncid, "X", NX,           &dimid[1]); CHK_ERR(st)

    /* Odd numbers are fixed variables, even numbers are record variables */
    for (i=0; i<NVARS; i++) {
        if (i%2) {
            snprintf(str, sizeof(str),"fixed_var_%d",i);
            st = nc_def_var(ncid, str, NC_INT, 1, dimid+1, &varid[i]); CHK_ERR(st)
        }
        else {
            snprintf(str, sizeof(str),"record_var_%d",i);
            st = nc_def_var(ncid, str, NC_INT, 2, dimid, &varid[i]); CHK_ERR(st)
        }
    }
    st = nc_enddef(ncid); CHK_ERR(st)

    /* Note NC_INDEPENDENT is the default */
    st = nc_var_par_access(ncid, NC_GLOBAL, NC_INDEPENDENT); CHK_ERR(st)

    /* write all variables */
    buf = (int*) malloc(NX * sizeof(int));
    for (i=0; i<NVARS; i++) {
        for (j=0; j<NX; j++) buf[j] = i*10 + j;
        if (i%2) {
            start[0] = 0; count[0] = NX;
            st = nc_put_vara_int(ncid, varid[i], start, count, buf); CHK_ERR(st)
        }
        else {
            start[0] = 0; start[1] = 0;
            count[0] = 1; count[1] = NX;
            st = nc_put_vara_int(ncid, varid[i], start, count, buf); CHK_ERR(st)
        }
    }
    st = nc_close(ncid); CHK_ERR(st)
    if (info != MPI_INFO_NULL) MPI_Info_free(&info);

    /* re-open the file with netCDF (parallel) and enter define mode */
    st = nc_open_par(file_name, NC_WRITE, comm, info, &ncid); CHK_ERR(st)

    st = nc_redef(ncid); CHK_ERR(st)

    /* add attributes to make header grow */
    for (i=0; i<NVARS; i++) {
        snprintf(str, sizeof(str), "annotation_for_var_%d",i);
        st = nc_put_att_text(ncid, varid[i], "text_attr", strlen(str), str); CHK_ERR(st)
    }
    st = nc_enddef(ncid); CHK_ERR(st)

    /* read variables and check their contents */
    for (i=0; i<NVARS; i++) {
        for (j=0; j<NX; j++) buf[j] = -1;
        if (i%2) {
            start[0] = 0; count[0] = NX;
            st = nc_get_var_int(ncid, varid[i], buf); CHK_ERR(st)
            for (j=0; j<NX; j++)
                if (buf[j] != i*10 + j)
                    printf("unexpected read value var i=%d buf[j=%d]=%d should be %d\n",i,j,buf[j],i*10+j);
        }
        else {
            start[0] = 0; start[1] = 0;
            count[0] = 1; count[1] = NX;
            st = nc_get_vara_int(ncid, varid[i], start, count, buf); CHK_ERR(st)
            for (j=0; j<NX; j++)
                if (buf[j] != i*10+j)
                    printf("unexpected read value var i=%d buf[j=%d]=%d should be %d\n",i,j,buf[j],i*10+j);
        }
    }
    st = nc_close(ncid); CHK_ERR(st)
fn_exit:
    free(buf);
    MPI_Finalize();
    err = nerrs;
    SUMMARIZE_ERR;
    FINAL_RESULTS;
    return (nerrs > 0);
}

/*
    Compile:
        mpicc -g -o nc_pnc nc_pnc.c -lnetcdf -lcurl -lhdf5_hl -lhdf5 -lpnetcdf -lz -lm

    Run:
        nc_pnc

    Standard Output:
        At the time of this test is written, I used the following libraries.
            HDF5    version 1.8.10
            netCDF  version 4.2.1.1 and
            PnetCDF version 1.3.1

        If macro DISABLE_PNETCDF_ALIGNMENT is defined (i.e. disable PnetCDF
        alignment) then there is no standard output.

        If macro DISABLE_PNETCDF_ALIGNMENT is NOT defined (i.e. default PnetCDF
        alignment) then this test reports unexpected read values below.

         unexpected read value var i=1 buf[j=0]=0 should be 10
         unexpected read value var i=1 buf[j=1]=0 should be 11
         unexpected read value var i=1 buf[j=2]=0 should be 12
         unexpected read value var i=1 buf[j=3]=0 should be 13
         unexpected read value var i=1 buf[j=4]=0 should be 14
         unexpected read value var i=3 buf[j=0]=0 should be 30
         unexpected read value var i=3 buf[j=1]=0 should be 31
         unexpected read value var i=3 buf[j=2]=0 should be 32
         unexpected read value var i=3 buf[j=3]=0 should be 33
         unexpected read value var i=3 buf[j=4]=0 should be 34
         unexpected read value var i=5 buf[j=0]=0 should be 50
         unexpected read value var i=5 buf[j=1]=0 should be 51
         unexpected read value var i=5 buf[j=2]=0 should be 52
         unexpected read value var i=5 buf[j=3]=0 should be 53
         unexpected read value var i=5 buf[j=4]=0 should be 54
*/
