/* This is a test for the NCEPLIBS-g2c project. 
 *
 * This test is for the g2c_write_index()/g2c_open_index() functions,
 * which write and read and index file that contians the byte offsets
 * to various parts of a GRIB2 file.
 *
 * Ed Hartnett 10/13/22
 */
#include "grib2_int.h"
#include <sys/time.h>

#define FILE_NAME "tst_index.txt"
#define WAVE_FILE "data/gdaswave.t00z.wcoast.0p16.f000.grib2"
#define REF_FILE "data/ref_gdaswave.t00z.wcoast.0p16.f000.grb2index"
#define INDEX_FILE "gdaswave.t00z.wcoast.0p16.f000.grb2index"
#define DEGRIB2_FILE "gdaswave.t00z.wcoast.0p16.f000.degrib2"
#define FTP_FILE "data/WW3_Regional_US_West_Coast_20220718_0000.grib2"
#define REF_FTP_FILE "data/ref_WW3_Regional_US_West_Coast_20220718_0000.grb2index"
#define MILLION 1000000

/** Subtract the `struct timeval' values X and Y, storing the result in
   RESULT.  Return 1 if the difference is negative, otherwise 0.  This
   function from the GNU documentation. */
int
nc4_timeval_subtract (result, x, y)
   struct timeval *result, *x, *y;
{
   /* Perform the carry for the later subtraction by updating Y. */
   if (x->tv_usec < y->tv_usec) {
      int nsec = (y->tv_usec - x->tv_usec) / MILLION + 1;
      y->tv_usec -= MILLION * nsec;
      y->tv_sec += nsec;
   }
   if (x->tv_usec - y->tv_usec > MILLION) {
      int nsec = (x->tv_usec - y->tv_usec) / MILLION;
      y->tv_usec += MILLION * nsec;
      y->tv_sec -= nsec;
   }

   /* Compute the time remaining to wait.
      `tv_usec' is certainly positive. */
   result->tv_sec = x->tv_sec - y->tv_sec;
   result->tv_usec = x->tv_usec - y->tv_usec;

   /* Return 1 if result is negative. */
   return x->tv_sec < y->tv_sec;
}

int
main()
{
    printf("Testing g2c index functions.\n");
#ifdef JPEG
    printf("Testing g2c_open_index() on file %s...", WAVE_FILE);
    {
        int g2cid;
        int num_msg;
        int ret;

        /* Will not work. */
        if (g2c_open_index(NULL, NULL, 0, &g2cid) != G2C_EINVAL)
            return G2C_ERROR;
        if (g2c_open_index("bad", NULL, 0, &g2cid) != G2C_EINVAL)
            return G2C_ERROR;
        if (g2c_open_index("bad", "bad", 0, &g2cid) != G2C_EFILE)
            return G2C_ERROR;

        /* Open the data file using the index file. */
        /* g2c_set_log_level(10); */
        if ((ret = g2c_open_index(WAVE_FILE, REF_FILE, 0, &g2cid)))
            return ret;

        /* Check some stuff. */
        if ((ret = g2c_inq(g2cid, &num_msg)))
            return ret;
        if (num_msg != 19)
            return G2C_ERROR;

        /* Output a degrib2 file. See tst_degrib2.c for a version of
         * this test that also checks the degrib2 output for
         * correctness. For this test, we just ensure it runs without
         * error. */
        if ((ret = g2c_degrib2(g2cid, DEGRIB2_FILE)))
            return ret;

        /* Close the file. */
        if ((ret = g2c_close(g2cid)))
            return ret;
    }
    printf("ok!\n");
    printf("Testing g2c_write_index() on file %s...", WAVE_FILE);
    {
        int g2cid;
        int num_msg;
        int ret;

        /* Open the data file. */
        if ((ret = g2c_open(WAVE_FILE, 0, &g2cid)))
            return ret;

        /* Check some stuff. */
        if ((ret = g2c_inq(g2cid, &num_msg)))
            return ret;
        if (num_msg != 19)
            return G2C_ERROR;

        /* These won't work. */
        if (g2c_write_index(-1, 0, INDEX_FILE) != G2C_EBADID)
            return G2C_ERROR;
        if (g2c_write_index(G2C_MAX_FILES + 1, 0, INDEX_FILE) != G2C_EBADID)
            return G2C_ERROR;
        if (g2c_write_index(10, 0, INDEX_FILE) != G2C_EBADID)
            return G2C_ERROR;
        if (g2c_write_index(g2cid, 0, NULL) != G2C_EINVAL)
            return G2C_ERROR;

        /* Write an index file. */
        if ((ret = g2c_write_index(g2cid, 0, INDEX_FILE)))
            return ret;

        /* Make sure NOCLOBBER works. */
        if (g2c_write_index(g2cid, G2C_NOCLOBBER, INDEX_FILE) != G2C_EFILE)
            return G2C_ERROR;
        
        /* Close the file. */
        if ((ret = g2c_close(g2cid)))
            return ret;
    }
    printf("ok!\n");
    printf("Testing whether we can read an index generated by g2c_write_index() on file %s...", WAVE_FILE);
    {
	int g2cid;
        int num_msg;
        int ret;

	/* Open the data file. */
        /* g2c_set_log_level(20); */
	if ((ret = g2c_open(WAVE_FILE, 0, &g2cid)))
	    return ret;

        /* Check some stuff. */
        if ((ret = g2c_inq(g2cid, &num_msg)))
            return ret;
        if (num_msg != 19)
            return G2C_ERROR;

        /* Write an index file. */
	if ((ret = g2c_write_index(g2cid, 0, INDEX_FILE)))
	    return ret;

        /* Close the file. */
	if ((ret = g2c_close(g2cid)))
	    return ret;

        /* Now reopen the file using the index we just generated. */
	if ((ret = g2c_open_index(WAVE_FILE, INDEX_FILE, 0, &g2cid)))
	    return ret;

        /* Check some stuff. */
        if ((ret = g2c_inq(g2cid, &num_msg)))
            return ret;
        if (num_msg != 19)
            return G2C_ERROR;

        /* Close the file. */
	if ((ret = g2c_close(g2cid)))
	    return ret;
    }
    printf("ok!\n");
#ifdef FTP_TEST_FILES
    printf("Testing g2c_open_index() on file %s downloaded via FTP...", FTP_FILE);
    {
	int g2cid;
        int num_msg;
        unsigned char sig_ref_time, month, day, hour, minute, second;
        short year;
        short center, subcenter;
        unsigned char master_version, local_version;
        int num_fields, num_local;
        unsigned char discipline;
	int ret;

	/* g2c_set_log_level(11); */
	/* Open the data file using the index file. */
	if ((ret = g2c_open_index(FTP_FILE, REF_FTP_FILE, 0, &g2cid)))
	    return ret;

        /* Check some stuff. */
        if ((ret = g2c_inq(g2cid, &num_msg)))
            return ret;
        if (num_msg != 688)
            return G2C_ERROR;

        /* Check some stuff about the last message. */
        if ((ret = g2c_inq_msg(g2cid, 687, &discipline, &num_fields, &num_local,
                               &center, &subcenter, &master_version, &local_version)))
            return ret;
        if (center != 7 || subcenter != 0 || master_version != 2 || local_version != 1)
            return G2C_ERROR;
        if (num_local || num_fields != 1 || discipline != 10)
            return G2C_ERROR;

        /* Inquire about the date/time. */
        if ((ret = g2c_inq_msg_time(g2cid, 687, &sig_ref_time, &year, &month, &day, &hour,
                                    &minute, &second)))
            return ret;

        /* Check date/time. */
        if (sig_ref_time != 1 || year != 2022 || month != 7 || day != 18 ||
            hour != 0 || minute != 0 || second != 0)
            return G2C_ERROR;

        /* Close the file. */
	if ((ret = g2c_close(g2cid)))
	    return ret;
    }
    printf("ok!\n");
    printf("Testing speed of g2c_open_index() on file %s downloaded via FTP...\n", FTP_FILE);
    {
	int g2cid;
        int num_msg;
        struct timeval start_time, end_time, diff_time;
        int open_1_us, open_2_us;
	int ret;

	/* Open the data file without using the index file. */
        if (gettimeofday(&start_time, NULL))
            return G2C_ERROR;
	if ((ret = g2c_open(FTP_FILE, 0, &g2cid)))
	    return ret;
        if (gettimeofday(&end_time, NULL)) 
            return G2C_ERROR;
        if (nc4_timeval_subtract(&diff_time, &end_time, &start_time))
            return G2C_ERROR;
        open_1_us = (int)diff_time.tv_sec * MILLION + (int)diff_time.tv_usec;
        
        /* Check some stuff. */
        if ((ret = g2c_inq(g2cid, &num_msg)))
            return ret;
        if (num_msg != 688)
            return G2C_ERROR;

        /* Close the file. */
	if ((ret = g2c_close(g2cid)))
	    return ret;

	/* Open the data file using the index file. */
        if (gettimeofday(&start_time, NULL))
            return G2C_ERROR;
	if ((ret = g2c_open_index(FTP_FILE, REF_FTP_FILE, 0, &g2cid)))
	    return ret;
        if (gettimeofday(&end_time, NULL)) 
            return G2C_ERROR;
        if (nc4_timeval_subtract(&diff_time, &end_time, &start_time)) 
            return G2C_ERROR;
        open_2_us = (int)diff_time.tv_sec * MILLION + (int)diff_time.tv_usec;
        printf("open without index %d with index %d, using index saved %d microseconds\n",
               open_1_us, open_2_us, open_1_us - open_2_us);

        /* Check some stuff. */
        if ((ret = g2c_inq(g2cid, &num_msg)))
            return ret;
        if (num_msg != 688)
            return G2C_ERROR;

        /* Close the file. */
	if ((ret = g2c_close(g2cid)))
	    return ret;
    }
    printf("ok!\n");
#endif /* FTP_TEST_FILES */
#endif /* JPEG */
    printf("SUCCESS!\n");
    return 0;
}

