/***************************************************************************
 * A program for libmseed parsing tests.
 *
 * This file is part of the miniSEED Library.
 *
 * Copyright (c) 2024 Chad Trabant, EarthScope Data Services
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ***************************************************************************/

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

#include <libmseed.h>

#define PACKAGE "lm_parse"
#define VERSION "[libmseed " LIBMSEED_VERSION " " PACKAGE " ]"

static flag verbose      = 0;
static flag ppackets     = 0;
static flag basicsum     = 0;
static flag tracegap     = 0;
static flag splitversion = 0;
static int printraw      = 0;
static int printdata     = 0;
static int reclen        = -1;
static char *inputfile   = NULL;

static int parameter_proc (int argcount, char **argvec);
static void usage (void);

/* Binary I/O for Windows platforms */
#ifdef LMP_WIN
  #include <fcntl.h>
  unsigned int _CRT_fmode = _O_BINARY;
#endif

int
main (int argc, char **argv)
{
  MS3TraceList *mstl = 0;
  MS3Record *msr = 0;
  uint32_t flags = 0;

  int64_t totalrecs = 0;
  int64_t totalsamps = 0;
  int retcode;

  /* Process command line arguments */
  if (parameter_proc (argc, argv) < 0)
    return -1;

  /* Validate CRC and unpack data samples */
  flags |= MSF_VALIDATECRC;

  /* Parse byte range from file/URL path name if present */
  flags |= MSF_PNAMERANGE;

  if (printdata)
    flags |= MSF_UNPACKDATA;

  if (tracegap)
    mstl = mstl3_init (NULL);

  /* Loop over the input file */
  while ((retcode = ms3_readmsr (&msr, inputfile, flags, verbose)) == MS_NOERROR)
  {
    totalrecs++;
    totalsamps += msr->samplecnt;

    if (tracegap)
    {
      mstl3_addmsr (mstl, msr, splitversion, 1, flags, NULL);
    }
    else
    {
      if ( printraw )
      {
        if (msr->formatversion == 3)
          ms_parse_raw3 (msr->record, msr->reclen, ppackets);
        else
          ms_parse_raw2 (msr->record, msr->reclen, ppackets, -1);
      }
      else
      {
        msr3_print (msr, ppackets);
      }

      if (printdata && msr->numsamples > 0)
      {
        int line, col, cnt, samplesize;
        int lines = (msr->numsamples / 6) + 1;
        void *sptr;

        if ((samplesize = ms_samplesize (msr->sampletype)) == 0)
        {
          ms_log (2, "Unrecognized sample type: '%c'\n", msr->sampletype);
        }
        if (msr->sampletype == 't')
        {
          char *text = (char *)msr->datasamples;
          int length  = msr->numsamples;

          ms_log (0, "Text data:\n");

          /* Print maximum log message segments */
          while (length > (MAX_LOG_MSG_LENGTH - 1))
          {
            ms_log (0, "%.*s", (MAX_LOG_MSG_LENGTH - 1), text);
            text += MAX_LOG_MSG_LENGTH - 1;
            length -= MAX_LOG_MSG_LENGTH - 1;
          }

          /* Print any remaining TEXT and add a newline */
          if (length > 0)
          {
            ms_log (0, "%.*s\n", length, text);
          }
          else
          {
            ms_log (0, "\n");
          }
        }
        else
          for (cnt = 0, line = 0; line < lines; line++)
          {
            for (col = 0; col < 6; col++)
            {
              if (cnt < msr->numsamples)
              {
                sptr = (char *)msr->datasamples + (cnt * samplesize);

                if (msr->sampletype == 'i')
                  ms_log (0, "%10d  ", *(int32_t *)sptr);

                else if (msr->sampletype == 'f')
                  ms_log (0, "%10.8g  ", *(float *)sptr);

                else if (msr->sampletype == 'd')
                  ms_log (0, "%10.10g  ", *(double *)sptr);

                cnt++;
              }
            }
            ms_log (0, "\n");

            /* If only printing the first 6 samples break out here */
            if (printdata == 1)
              break;
          }
      }
    }
  }

  if (retcode != MS_ENDOFFILE)
    ms_log (2, "Cannot read %s: %s\n", inputfile, ms_errorstr (retcode));

  if (tracegap)
    mstl3_printtracelist (mstl, ISOMONTHDAY_Z, 1, 1, splitversion);

  /* Make sure everything is cleaned up */
  ms3_readmsr (&msr, NULL, flags, 0);

  if (mstl)
    mstl3_free (&mstl, 0);

  if (basicsum)
    ms_log (1, "Records: %" PRId64 ", Samples: %" PRId64 "\n",
            totalrecs, totalsamps);

  return 0;
} /* End of main() */

/***************************************************************************
 * parameter_proc():
 * Process the command line arguments.
 *
 * Returns 0 on success, and -1 on failure
 ***************************************************************************/
static int
parameter_proc (int argcount, char **argvec)
{
  int optind;

  /* Process all command line arguments */
  for (optind = 1; optind < argcount; optind++)
  {
    if (strcmp (argvec[optind], "-V") == 0)
    {
      ms_log (1, "%s version: %s\n", PACKAGE, VERSION);
      exit (0);
    }
    else if (strcmp (argvec[optind], "-h") == 0)
    {
      usage ();
      exit (0);
    }
    else if (strncmp (argvec[optind], "-v", 2) == 0)
    {
      verbose += strspn (&argvec[optind][1], "v");
    }
    else if (strncmp (argvec[optind], "-p", 2) == 0)
    {
      ppackets += strspn (&argvec[optind][1], "p");
    }
    else if (strncmp (argvec[optind], "-z", 2) == 0)
    {
      printraw = 1;
    }
    else if (strncmp (argvec[optind], "-d", 2) == 0)
    {
      printdata = 1;
    }
    else if (strncmp (argvec[optind], "-D", 2) == 0)
    {
      printdata = 2;
    }
    else if (strcmp (argvec[optind], "-P") == 0)
    {
      splitversion = 1;
    }
    else if (strncmp (argvec[optind], "-tg", 3) == 0)
    {
      tracegap = 1;
    }
    else if (strcmp (argvec[optind], "-s") == 0)
    {
      basicsum = 1;
    }
    else if (strcmp (argvec[optind], "-r") == 0)
    {
      reclen = atoi (argvec[++optind]);
    }
    else if (strncmp (argvec[optind], "-", 1) == 0 &&
             strlen (argvec[optind]) > 1)
    {
      ms_log (2, "Unknown option: %s\n", argvec[optind]);
      exit (1);
    }
    else if (inputfile == NULL)
    {
      inputfile = argvec[optind];
    }
    else
    {
      ms_log (2, "Unknown option: %s\n", argvec[optind]);
      exit (1);
    }
  }

  /* Make sure an inputfile was specified */
  if (!inputfile)
  {
    ms_log (2, "No input file was specified\n\n");
    ms_log (1, "%s version %s\n\n", PACKAGE, VERSION);
    ms_log (1, "Try %s -h for usage\n", PACKAGE);
    exit (1);
  }

  /* Add program name and version to User-Agent for URL-based requests */
  if (libmseed_url_support() && ms3_url_useragent(PACKAGE, VERSION))
    return -1;

  /* Report the program version */
  if (verbose)
    ms_log (1, "%s version: %s\n", PACKAGE, VERSION);

  return 0;
} /* End of parameter_proc() */

/***************************************************************************
 * usage():
 * Print the usage message and exit.
 ***************************************************************************/
static void
usage (void)
{
  fprintf (stderr, "%s version: %s\n\n", PACKAGE, VERSION);
  fprintf (stderr, "Usage: %s [options] file\n\n", PACKAGE);
  fprintf (stderr,
           " ## Options ##\n"
           " -V             Report program version\n"
           " -h             Show this usage message\n"
           " -v             Be more verbose, multiple flags can be used\n"
           " -p             Print details of header, multiple flags can be used\n"
           " -z             Print raw details of header\n"
           " -d             Print first 6 sample values\n"
           " -D             Print all sample values\n"
           " -P             Additionally group traces by data publication version\n"
           " -tg            Print trace listing with gap information\n"
           " -s             Print a basic summary after processing a file\n"
           " -r bytes       Specify record length in bytes, required if no Blockette 1000\n"
           "\n"
           " file           File of miniSEED records\n"
           "\n");
} /* End of usage() */
