/*
  This file is part of CDO. CDO is a collection of Operators to
  manipulate and analyse Climate model Data.

  Copyright (C) 2003-2020 Uwe Schulzweida, <uwe.schulzweida AT mpimet.mpg.de>
  See COPYING file for copying and redistribution conditions.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; version 2 of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
*/

/*
   This module contains the following operators:

      Sinfo      sinfo           Short dataset information
*/

#include <cdi.h>

#include <cstring>
#include "cdo_options.h"
#include "cdo_cdi_wrapper.h"
#include "cdi_uuid.h"
#include "printinfo.h"
#include "mpmo_color.h"
#include "process_int.h"
#include "compare.h"
#include "util_string.h"

const char *steptypeName(const int tsteptype);

static void
limitStringLength(char *string, size_t maxlen)
{
  string[maxlen - 1] = 0;
  const auto len = strlen(string);
  if (len > 10)
    {
      for (size_t i = 3; i < len; ++i)
        if (string[i] == ' ' || string[i] == ',' || (i > 10 && string[i] == '.'))
          {
            string[i] = 0;
            break;
          }
    }
}

enum
{
  func_generic,
  func_param,
  func_name,
  func_code
};

static void
addOperators(void)
{
  // clang-format off
  cdoOperatorAdd("sinfo",   func_generic, 0, nullptr);
  cdoOperatorAdd("sinfop",  func_param,   0, nullptr);
  cdoOperatorAdd("sinfon",  func_name,    0, nullptr);
  cdoOperatorAdd("sinfoc",  func_code,    0, nullptr);
  cdoOperatorAdd("seinfo",  func_generic, 1, nullptr);
  cdoOperatorAdd("seinfop", func_param,   1, nullptr);
  cdoOperatorAdd("seinfon", func_name,    1, nullptr);
  cdoOperatorAdd("seinfoc", func_code,    1, nullptr);
  // clang-format on
}

void *
Sinfo(void *process)
{
  char tmpname[CDI_MAX_NAME];
  char varname[CDI_MAX_NAME];
  char paramstr[32];

  cdoInitialize(process);

  addOperators();

  const auto operatorID = cdoOperatorID();

  const auto operfunc = cdoOperatorF1(operatorID);
  const auto lensemble = cdoOperatorF2(operatorID);

  operatorCheckArgc(0);

  for (int indf = 0; indf < cdoStreamCnt(); indf++)
    {
      const auto streamID = cdoOpenRead(indf);
      const auto vlistID = cdoStreamInqVlist(streamID);

      set_text_color(stdout, BRIGHT);
      fprintf(stdout, "   File format");
      reset_text_color(stdout);
      fprintf(stdout, " : ");
      printFiletype(streamID, vlistID);

      const auto nvars = vlistNvars(vlistID);
      const auto nsubtypes = vlistNsubtypes(vlistID);

      set_text_color(stdout, BRIGHT);
      fprintf(stdout, "%6d : Institut Source   T Steptype", -(indf + 1));
      if (lensemble) fprintf(stdout, " Einfo");
      if (nsubtypes > 1) fprintf(stdout, " Subtypes");
      fprintf(stdout, " Levels Num    Points Num Dtype : ");
      fprintf(stdout, "%s", (operfunc == func_name) ? "Parameter name" : ((operfunc == func_code) ? "Table Code" : "Parameter ID"));

      if (Options::cdoVerbose) fprintf(stdout, " : Extra");
      reset_text_color(stdout);
      fprintf(stdout, "\n");

      for (int varID = 0; varID < nvars; varID++)
        {
          const auto param = vlistInqVarParam(vlistID, varID);
          const auto code = vlistInqVarCode(vlistID, varID);
          const auto tabnum = tableInqNum(vlistInqVarTable(vlistID, varID));
          const auto gridID = vlistInqVarGrid(vlistID, varID);
          const auto zaxisID = vlistInqVarZaxis(vlistID, varID);

          fprintf(stdout, "%6d", varID + 1);
          fprintf(stdout, " : ");

          set_text_color(stdout, BLUE);
          // institute info
          auto instptr = institutInqNamePtr(vlistInqVarInstitut(vlistID, varID));
          strcpy(tmpname, "unknown");
          if (instptr) strncpy(tmpname, instptr, CDI_MAX_NAME - 1);
          limitStringLength(tmpname, 32);
          fprintf(stdout, "%-8s ", tmpname);

          // source info
          auto modelptr = modelInqNamePtr(vlistInqVarModel(vlistID, varID));
          strcpy(tmpname, "unknown");
          if (modelptr) strncpy(tmpname, modelptr, CDI_MAX_NAME - 1);
          limitStringLength(tmpname, 32);
          fprintf(stdout, "%-8s ", tmpname);

          // timetype
          const auto timetype = vlistInqVarTimetype(vlistID, varID);
          fprintf(stdout, "%c ", timetype == TIME_CONSTANT ? 'c' : 'v');

          // tsteptype
          const auto tsteptype = vlistInqVarTsteptype(vlistID, varID);
          fprintf(stdout, "%-8s ", steptypeName(tsteptype));

          // ensemble information
          if (lensemble)
            {
              int perturbationNumber, numberOfForecastsInEnsemble;
              const auto r1 = cdiInqKeyInt(vlistID, varID, CDI_KEY_PERTURBATIONNUMBER, &perturbationNumber);
              const auto r2 = cdiInqKeyInt(vlistID, varID, CDI_KEY_NUMBEROFFORECASTSINENSEMBLE, &numberOfForecastsInEnsemble);
              if (r1 == 0 && r2 == 0)
                fprintf(stdout, "%2d/%-2d ", perturbationNumber, numberOfForecastsInEnsemble);
              else
                fprintf(stdout, "--/-- ");
            }

          if (nsubtypes > 1)
            {
              const auto subtypeID = vlistInqVarSubtype(vlistID, varID);
              const auto subtypesize = subtypeInqSize(subtypeID);
              fprintf(stdout, " %6d  ", subtypesize);
              fprintf(stdout, "%3d ", vlistSubtypeIndex(vlistID, subtypeID) + 1);
            }
          reset_text_color(stdout);

          // layer info
          const auto levelsize = zaxisInqSize(zaxisID);
          set_text_color(stdout, GREEN);
          fprintf(stdout, "%6d ", levelsize);
          reset_text_color(stdout);
          fprintf(stdout, "%3d ", vlistZaxisIndex(vlistID, zaxisID) + 1);

          // grid info
          const auto gridsize = gridInqSize(gridID);
          set_text_color(stdout, GREEN);
          fprintf(stdout, "%9zu ", gridsize);
          reset_text_color(stdout);
          fprintf(stdout, "%3d ", vlistGridIndex(vlistID, gridID) + 1);

          // datatype
          const auto datatypestr = cdi_datatype2str(vlistInqVarDatatype(vlistID, varID));
          set_text_color(stdout, BLUE);
          fprintf(stdout, " %-3s", datatypestr);

          const auto comptype = vlistInqVarCompType(vlistID, varID);
          if (comptype == CDI_COMPRESS_NONE)
            fprintf(stdout, "  ");
          else
            fprintf(stdout, "%c ", (int) comptypeToName(comptype)[0]);

          reset_text_color(stdout);

          fprintf(stdout, ": ");

          // parameter info
          cdiParamToString(param, paramstr, sizeof(paramstr));

          if (operfunc == func_name) vlistInqVarName(vlistID, varID, varname);

          // set_text_color(stdout, GREEN);
          if (operfunc == func_name)
            fprintf(stdout, "%-14s", varname);
          else if (operfunc == func_code)
            fprintf(stdout, "%4d %4d   ", tabnum, code);
          else
            fprintf(stdout, "%-14s", paramstr);
          // reset_text_color(stdout);

          if (Options::cdoVerbose)
            {
              char varextra[CDI_MAX_NAME];
              vlistInqVarExtra(vlistID, varID, varextra);
              fprintf(stdout, " : %s", varextra);
            }

          fprintf(stdout, "\n");
        }

      set_text_color(stdout, BRIGHT);
      fprintf(stdout, "   Grid coordinates");
      reset_text_color(stdout);
      fprintf(stdout, " :\n");

      printGridInfo(vlistID);

      set_text_color(stdout, BRIGHT);
      fprintf(stdout, "   Vertical coordinates");
      reset_text_color(stdout);
      fprintf(stdout, " :\n");

      printZaxisInfo(vlistID);

      if (nsubtypes > 1)
        {
          fprintf(stdout, "   Subtypes");
          fprintf(stdout, " :\n");

          printSubtypeInfo(vlistID);
        }

      const auto taxisID = vlistInqTaxis(vlistID);
      const auto ntsteps = vlistNtsteps(vlistID);

      if (ntsteps != 0)
        {
          set_text_color(stdout, BRIGHT);
          fprintf(stdout, "   Time coordinate");
          reset_text_color(stdout);
          fprintf(stdout, " :  ");
          set_text_color(stdout, GREEN);
          if (ntsteps == CDI_UNDEFID)
            fprintf(stdout, "unlimited steps\n");
          else
            fprintf(stdout, "%d step%s\n", ntsteps, ntsteps == 1 ? "" : "s");
          reset_text_color(stdout);

          if (taxisID != CDI_UNDEFID)
            {
              if (taxisInqType(taxisID) != TAXIS_ABSOLUTE)
                {
                  auto vdate = taxisInqRdate(taxisID);
                  auto vtime = taxisInqRtime(taxisID);
                  fprintf(stdout, "     RefTime = %s %s", dateToString(vdate).c_str(), timeToString(vtime).c_str());

                  const auto tunits = taxisInqTunit(taxisID);
                  if (tunits != CDI_UNDEFID) fprintf(stdout, "  Units = %s", tunitToCstr(tunits));

                  const auto calendar = taxisInqCalendar(taxisID);
                  if (calendar != CDI_UNDEFID) fprintf(stdout, "  Calendar = %s", calendarToCstr(calendar));

                  if (taxisHasBounds(taxisID)) fprintf(stdout, "  Bounds = true");

                  fprintf(stdout, "\n");

                  if (taxisInqType(taxisID) == TAXIS_FORECAST)
                    {
                      vdate = taxisInqFdate(taxisID);
                      vtime = taxisInqFtime(taxisID);
                      fprintf(stdout, "     ForecastRefTime = %s %s", dateToString(vdate).c_str(), timeToString(vtime).c_str());
                      fprintf(stdout, "\n");
                    }
                }
            }

          fprintf(stdout, "  YYYY-MM-DD hh:mm:ss  YYYY-MM-DD hh:mm:ss  YYYY-MM-DD hh:mm:ss  YYYY-MM-DD hh:mm:ss\n");

          set_text_color(stdout, MAGENTA);
          printTimesteps(streamID, taxisID, Options::cdoVerbose);
          reset_text_color(stdout);
          fprintf(stdout, "\n");
        }

      cdoStreamClose(streamID);
    }

  cdoFinish();

  return nullptr;
}
