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

  Author: Uwe Schulzweida

*/

#include "cdo_varlist.h"
#include "cdo_output.h"
#include "util_string.h"
#include "compare.h"
#include "stdnametable.h"

static bool
isIntType(int dataType)
{
  return (dataType == CDI_DATATYPE_UINT8 || dataType == CDI_DATATYPE_UINT16 || dataType == CDI_DATATYPE_INT16);
}

static bool
isFloatType(int dataType)
{
  return (dataType == CDI_DATATYPE_FLT32 || dataType == CDI_DATATYPE_CPX32);
}

void
varListInit(VarList &vl, int vlistID)
{
  auto nvars = vlistNvars(vlistID);
  vl.resize(nvars);

  for (int varID = 0; varID < nvars; ++varID)
    {
      vlistInqVarName(vlistID, varID, vl[varID].name);
      vl[varID].gridID = vlistInqVarGrid(vlistID, varID);
      vl[varID].zaxisID = vlistInqVarZaxis(vlistID, varID);
      vl[varID].timetype = vlistInqVarTimetype(vlistID, varID);
      vl[varID].tsteptype = vlistInqVarTsteptype(vlistID, varID);
      vl[varID].gridsize = gridInqSize(vl[varID].gridID);
      vl[varID].nlevels = zaxisInqSize(vl[varID].zaxisID);
      vl[varID].datatype = vlistInqVarDatatype(vlistID, varID);
      vl[varID].missval = vlistInqVarMissval(vlistID, varID);
      vl[varID].code = vlistInqVarCode(vlistID, varID);
      vl[varID].param = vlistInqVarParam(vlistID, varID);
      vl[varID].nwpv = vlistInqVarNumber(vlistID, varID);
      if (Options::CDO_Memtype == MemType::Native)
        {
          double addoffset = 0.0, scalefactor = 1.0;
          auto haveAddoffset = (cdiInqKeyFloat(vlistID, varID, CDI_KEY_ADDOFFSET, &addoffset) == CDI_NOERR);
          auto haveScalefactor = (cdiInqKeyFloat(vlistID, varID, CDI_KEY_SCALEFACTOR, &scalefactor) == CDI_NOERR);
          auto isPacked = (haveAddoffset || haveScalefactor);
          auto useFloatType = isFloatType(vl[varID].datatype) || (isIntType(vl[varID].datatype) && !isPacked);
          vl[varID].memType = useFloatType ? MemType::Float : MemType::Double;
        }
      else
        {
          vl[varID].memType = Options::CDO_Memtype;
        }
    }
}

void
varListSetUniqueMemtype(VarList &vl)
{
  int nvars = vl.size();
  if (nvars)
    {
      auto memtype = vl[0].memType;
      int varID;
      for (varID = 1; varID < nvars; ++varID)
        {
          if (vl[varID].memType != memtype) break;
        }
      if (varID < nvars) varListSetMemtype(vl, MemType::Double);
    }
}

void
varListSetMemtype(VarList &vl, MemType memType)
{
  for (auto &var : vl) var.memType = memType;
}

VarIDs
search_varIDs(const VarList &varList, int vlistID, int numFullLevels)
{
  VarIDs varIDs;

  auto nvars = vlistNvars(vlistID);

  auto useTable = false;
  for (int varID = 0; varID < nvars; ++varID)
    {
      auto tableNum = tableInqNum(vlistInqVarTable(vlistID, varID));
      if (tableNum > 0 && tableNum < 255)
        {
          useTable = true;
          break;
        }
    }

  if (Options::cdoVerbose && useTable) cdo_print("Using code tables!");

  char paramstr[32];
  gribcode_t gribcodes;

  for (int varID = 0; varID < nvars; ++varID)
    {
      auto nlevels = varList[varID].nlevels;
      auto instNum = institutInqCenter(vlistInqVarInstitut(vlistID, varID));
      auto tableNum = tableInqNum(vlistInqVarTable(vlistID, varID));

      auto code = varList[varID].code;

      auto param = varList[varID].param;
      cdiParamToString(param, paramstr, sizeof(paramstr));
      int pnum, pcat, pdis;
      cdiDecodeParam(param, &pnum, &pcat, &pdis);
      if (pdis >= 0 && pdis < 255) code = -1;

      if (useTable)
        {
          if (tableNum == 2)
            {
              wmo_gribcodes(&gribcodes);
            }
          else if (tableNum == 128 || tableNum == 0 || tableNum == 255)
            {
              echam_gribcodes(&gribcodes);
            }
          //  KNMI: HIRLAM model version 7.2 uses tableNum=1    (LAMH_D11*)
          //  KNMI: HARMONIE model version 36 uses tableNum=1   (grib*) (opreational NWP version)
          //  KNMI: HARMONIE model version 38 uses tableNum=253 (grib,grib_md) and tableNum=1 (grib_sfx) (research version)
          else if (tableNum == 1 || tableNum == 253)
            {
              hirlam_harmonie_gribcodes(&gribcodes);
            }
        }
      else
        {
          echam_gribcodes(&gribcodes);
        }

      if (Options::cdoVerbose)
        cdo_print("Center=%d  TableNum=%d  Code=%d  Param=%s  Varname=%s  varID=%d", instNum,
                  tableNum, code, paramstr, varList[varID].name, varID);

      if (code <= 0 || code == 255)
        {
          char varname[CDI_MAX_NAME];
          vlistInqVarName(vlistID, varID, varname);
          cstr_to_lower_case(varname);

          char stdname[CDI_MAX_NAME];
          int length = CDI_MAX_NAME;
          cdiInqKeyString(vlistID, varID, CDI_KEY_STDNAME, stdname, &length);
          cstr_to_lower_case(stdname);

          code = stdname_to_echamcode(stdname);
          if (code == -1)
            {
              //                                             ECHAM                         ECMWF
              // clang-format off
              if      (-1 == varIDs.sgeopotID && (cdo_cmpstr(varname, "geosp") || cdo_cmpstr(varname, "z"))) code = gribcodes.geopot;
              else if (-1 == varIDs.tempID    && (cdo_cmpstr(varname, "st")    || cdo_cmpstr(varname, "t"))) code = gribcodes.temp;
              else if (-1 == varIDs.psID      && (cdo_cmpstr(varname, "aps")   || cdo_cmpstr(varname, "sp"))) code = gribcodes.ps;
              else if (-1 == varIDs.psID      &&  cdo_cmpstr(varname, "ps")) code = gribcodes.ps;
              else if (-1 == varIDs.lnpsID    && (cdo_cmpstr(varname, "lsp")   || cdo_cmpstr(varname, "lnsp"))) code = gribcodes.lsp;
              else if (-1 == varIDs.lnpsID2   &&  cdo_cmpstr(varname, "lnps")) code = 777;
              else if (-1 == varIDs.geopotID  &&  cdo_cmpstr(stdname, "geopotential_full")) code = gribcodes.geopot;
              else if (-1 == varIDs.tempID    &&  cdo_cmpstr(varname, "t")) code = gribcodes.temp;
              else if (-1 == varIDs.humID     &&  cdo_cmpstr(varname, "q")) code = gribcodes.hum;
              // else if (cdo_cmpstr(varname, "clwc")) code = 246;
              // else if (cdo_cmpstr(varname, "ciwc")) code = 247;
              // clang-format on
            }
        }

      // clang-format off
      if      (code == gribcodes.geopot  && nlevels == 1) varIDs.sgeopotID = varID;
      else if (code == gribcodes.geopot  && nlevels == numFullLevels) varIDs.geopotID = varID;
      else if (code == gribcodes.temp    && nlevels == numFullLevels) varIDs.tempID = varID;
      else if (code == gribcodes.ps      && nlevels == 1) varIDs.psID = varID;
      else if (code == gribcodes.lsp     && nlevels == 1) varIDs.lnpsID = varID;
      else if (code == 777               && nlevels == 1) varIDs.lnpsID2 = varID;
      else if (code == gribcodes.gheight && nlevels == numFullLevels) varIDs.gheightID = varID;
      else if (code == gribcodes.hum     && nlevels == numFullLevels) varIDs.humID = varID;
      // else if (code == 246 && nlevels == numFullLevels) varIDs.clwcID = varID;
      // else if (code == 247 && nlevels == numFullLevels) varIDs.ciwcID = varID;
      // clang-format on
    }

  return varIDs;
}
