#ifdef HAVE_CONFIG_H
#include "../src/config.h"  /* The size of off_t depends on _FILE_OFFSET_BITS (on SUN) */
#endif

#ifdef HAVE_SYS_TIMES_H
#include <sys/time.h>
#include <sys/times.h>
#endif

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

#include "cgribex.h"

#define  UNDEFID  -1
#define  JP23SET    0x7FFFFF  // 2**23 - 1 (---> 8388607)

static char *Progname;

static
void usage(void)
{
  fprintf(stderr, "usage : %s [-P <nthread>]  ifile  ofile\n", Progname);
}


double util_walltime(void)
{
  double time_in_secs = 0.0;
#if defined(HAVE_SYS_TIMES_H)
  static double time_init = 0.;

  struct timeval tbuf;
  if ( gettimeofday(&tbuf,NULL) == -1 ) perror("UTIL_WALLTIME");

  // if ( time_init == 0.0 )
  if ( !(time_init < 0.0 || 0.0 < time_init) )
    time_init = (double) tbuf.tv_sec + ((double)tbuf.tv_usec * 1.0e-6);

  time_in_secs = (double) tbuf.tv_sec + ((double)tbuf.tv_usec * 1.0e-6) - time_init;
#endif

  return (time_in_secs);
}
 

int main(int argc, char *argv[])
{
  char *ifile = NULL;
  char *ofile = NULL;
  int ierr;
  int nrec = 0;
  unsigned char *igribbuffer = NULL;
  unsigned char *itmpbuffer = NULL;
  unsigned char *ogribbuffer = NULL;
  unsigned char *buffer = NULL;
  size_t igribbuffersize = 0;
  size_t itmpbuffersize = 0;
  size_t buffersize = 0;
  size_t readsize;
  int lzip = 0, lunzip = 0;
  int izip;
  /* int nthreads = 1; */
  clock_t start, finish;
  double wstart, wfinish;
  double tread = 0, tcompress = 0, twrite = 0;
  double wread = 0, wcompress = 0, wwrite = 0;

  Progname = strrchr(argv[0], '/');
  if ( Progname == NULL ) Progname = argv[0];
  else                    Progname++;

  if      ( strcmp(Progname, "gribzip")   == 0 ) lzip = 1;
  else if ( strcmp(Progname, "gribunzip") == 0 ) lunzip = 1;
  else
    {
      fprintf(stderr, "Wrong program name!");
      return (1);
    }

  argc--;

  if ( argc == 4 )
    {
      if ( argv[1][0] == '-' && argv[1][1] == 'P' )
	{
	  /* nthreads = atoi(argv[2]); */
	}
      argc += 2;
      ++argv;
      ++argv;
    }

  //  printf("nthreads = %d\n", nthreads);

  if ( argc && !ifile )
    {
      ifile = (++argv)[0];
      argc--;
    }

  if ( argc && !ofile )
    {
      ofile = (++argv)[0];
      argc--;
    }

  if ( !ifile || !ofile )
    {
      usage();
      return (-1);
    }

  int ifileID = gribOpen(ifile, "r");
  if ( ifileID == UNDEFID )
    {
      fprintf(stderr, "Open failed on %s\n", ifile);
      return (1);
    }

  int ofileID = gribOpen(ofile, "w");
  if ( ofileID == UNDEFID )
    {
      fprintf(stderr, "Open failed on %s\n", ofile);
      return (1);
    }

  clock_t start0 = clock();
  double wstart0 = util_walltime();

  while ( 1 )
    {
      wstart = util_walltime();
      start = clock();

      size_t irecsize = gribGetSize(ifileID);

      if ( irecsize == 0 ) break;

      if ( igribbuffersize < (irecsize+1000) )
	{
	  igribbuffersize = irecsize+1000;
	  igribbuffer = (unsigned char *) realloc(igribbuffer, igribbuffersize);
	}

      readsize = irecsize;
      ierr = gribRead(ifileID, igribbuffer, &readsize);
      if ( ierr != 0 ) break;
      nrec++;
      finish = clock();
      wfinish = util_walltime();
      tread += ((double)(finish-start))/CLOCKS_PER_SEC;
      wread += wfinish-wstart;

      int gribversion = gribVersion(igribbuffer, readsize);

      size_t unzipsize;
      if ( lunzip && (izip = gribGetZip(irecsize, igribbuffer, &unzipsize)) > 0 )
	{
	  if ( izip == 128 || izip == 130 ) // szip or aec
	    {
	      if ( unzipsize < irecsize )
		{
		  fprintf(stderr,
                          "Decompressed size smaller than compressed size (in %zu; out %zu)!\n",
			  irecsize, unzipsize);
		  continue;
		}

	      if ( itmpbuffersize < irecsize )
		{
		  itmpbuffersize = irecsize;
		  itmpbuffer = (unsigned char *) realloc(itmpbuffer, itmpbuffersize);
		}

	      memcpy(itmpbuffer, igribbuffer, itmpbuffersize);

	      unzipsize += 100; // need 0 to 1 bytes for rounding of bds
	      if ( igribbuffersize < unzipsize )
		{
		  igribbuffersize = unzipsize;
		  igribbuffer = (unsigned char *) realloc(igribbuffer, igribbuffersize);
		}

	      irecsize = (size_t)gribUnzip(igribbuffer, (long)unzipsize, itmpbuffer, (long)irecsize);
	    }
	  else
	    {
	      fprintf(stderr, "Decompression for %d not implemented!\n", izip);
	    }
	}

      if ( ofile )
	{
	  size_t nbytes = irecsize;

	  if ( lzip && gribversion == 1 )
	    {
	      if ( buffersize < igribbuffersize )
		{
		  buffersize = igribbuffersize;
		  buffer = (unsigned char *) realloc(buffer, buffersize);
		}
	      wstart = util_walltime();
	      start = clock();
	      nbytes = (size_t)gribZip(igribbuffer, (long)igribbuffersize, buffer, (long)buffersize);
	      finish = clock();
	      wfinish = util_walltime();
	      tcompress += ((double)(finish-start))/CLOCKS_PER_SEC;
	      wcompress += wfinish-wstart;
	    }

	  if ( nbytes > JP23SET )
	    while ( nbytes%120 ) igribbuffer[nbytes++] = 0;
	  else
	    while ( nbytes & 7 ) igribbuffer[nbytes++] = 0;
	  
	  wstart = util_walltime();
	  start = clock();
	  (void) gribWrite(ofileID, igribbuffer, nbytes);
	  finish = clock();
	  wfinish = util_walltime();
	  twrite += ((double)(finish-start))/CLOCKS_PER_SEC;
	  wwrite += wfinish-wstart;
	}
    }

  if ( ofile ) gribClose(ofileID);

  gribClose(ifileID);

  clock_t finish0 = clock();
  double wfinish0 = util_walltime();

  double tall = ((double)(finish0-start0))/CLOCKS_PER_SEC;
  double tsum = tread + tcompress + twrite;
  double wall = wfinish0-wstart0;
  double wsum = wread + wcompress + wwrite;

  printf("tall=%.2f tsum=%.2f tread=%.2f tcompress=%.2f twrite=%.2f\n", tall, tsum, tread, tcompress, twrite);
  printf("wall=%.2f wsum=%.2f wread=%.2f wcompress=%.2f wwrite=%.2f\n", wall, wsum, wread, wcompress, wwrite);
  if ( igribbuffer ) free(igribbuffer);
  if ( itmpbuffer  ) free(itmpbuffer);
  if ( ogribbuffer ) free(ogribbuffer);
  if ( buffer ) free(buffer);

  return (0);
}
