#ifndef CDO_VARLIST_H
#define CDO_VARLIST_H

#include <cassert>
#include <string>
#include <vector>
#include <map>

#include <cdi.h>

#include "cdo_options.h"

enum class MapFlag
{
  Undefined = 0,
  Left = 1,
  Right = 2,
  Intersect = 3
};

namespace CmpVarList
{
constexpr int Name = 1;
constexpr int Grid = 2;
constexpr int NumLevels = 4;
constexpr int GridSize = 8;
constexpr int Dim = GridSize | NumLevels | Grid;
constexpr int All = Name | Dim;
};  // namespace CmpVarList

struct CdoVar
{
  std::string name;
  std::string longname;
  std::string units;
  std::string stdname;
  MemType memType{ MemType::Native };
  int gridID{ -1 };
  int zaxisID{ -1 };
  int gridType{ -1 };
  int zaxisType{ -1 };
  int timeType{ -1 };
  int stepType{ -1 };
  size_t gridsize{ 0 };
  int nlevels{ 0 };
  int dataType{ -1 };
  double missval{ 0 };
  int code{ 0 };
  int param{ 0 };
  int nwpv{ 1 };  // number of words per value; real:1  complex:2
  bool isConstant{ false };
  int ID{ 0 };
};

using CdoVars = std::vector<CdoVar>;

void cdoVars_init(CdoVars &cdoVars, int vlistID);

class VarList
{
public:
  CdoVars vars;
  int vlistID{ CDI_UNDEFID };

  VarList() {}
  VarList(int _vlistID) : vlistID(_vlistID)
  {
    cdoVars_init(vars, _vlistID);
    m_maxFields = vlistNumFields(_vlistID);
    m_numSteps = vlistNtsteps(_vlistID);
    m_numConstVars = num_const_vars(vars);
    m_numVaryingVars = num_varying_vars(vars);
  }

  // clang-format off
  void isInit() const{ assert(vlistID != CDI_UNDEFID); }
  int numVars() const noexcept { isInit(); return static_cast<int>(vars.size()); }
  int maxFields() const noexcept { isInit(); return m_maxFields; }
  int numSteps() const noexcept { isInit(); return m_numSteps; }
  int numConstVars() const noexcept { isInit(); return m_numConstVars; }
  int numVaryingVars() const noexcept { isInit(); return m_numVaryingVars; }
  // clang-format on

private:
  // clang-format off
  int m_maxFields{ 0 };
  int m_numSteps{ 0 };
  int m_numConstVars{ 0 };
  int m_numVaryingVars{ 0 };
  int num_const_vars(const CdoVars &cdoVars);
  int num_varying_vars(const CdoVars &cdoVars);
  // clang-format on
};

struct VarIDs
{
  int sgeopotID{ CDI_UNDEFID };
  int geopotID{ CDI_UNDEFID };
  int tempID{ CDI_UNDEFID };
  int psID{ CDI_UNDEFID };
  int lnpsID{ CDI_UNDEFID };
  int lnpsID2{ CDI_UNDEFID };
  int gheightID{ CDI_UNDEFID };
  int humID{ CDI_UNDEFID };
  int clwcID{ CDI_UNDEFID };
  int ciwcID{ CDI_UNDEFID };
};

VarIDs varList_search_varIDs(const VarList &varList, int numFullLevels);

void vlist_compare(int vlistID1, int vlistID2, int cmpFlag);

void varList_compare(const VarList &varList1, const VarList &varList2, int cmpFlag = CmpVarList::All);
void varList_map(const VarList &varList1, const VarList &varList2, MapFlag mapFlag, std::map<int, int> &mapOfVarIDs);

void varList_set_memtype(VarList &varList, MemType memType);
void varList_set_unique_memtype(VarList &varList);
int varList_get_psvarid(const VarList &varList, int zaxisID);

#endif
