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

  Author: Uwe Schulzweida

*/

#include <cassert>
#include <cmath>

#include "param_conversion.h"
#include "selectkey.h"
#include "util_wildcards.h"
#include "util_string.h"
#include "cdo_output.h"

template <>
std::vector<int>
SelectKey<int>::get_values(KeyValues const &kv)
{
  std::vector<int> v;
  v.reserve(kv.nvalues);
  for (int i = 0, n = kv.nvalues; i < n; ++i)
  {
    int first{}, last{}, inc{};
    split_intstring(kv.values[i], first, last, inc);

    if (first == last) { v.push_back(first); }
    else
    {
      if (inc == 0) cdo_abort("SelectKey: step size 0 is invalid for range %d to %d!", first, last);

      if (inc >= 0)
        for (int ival = first; ival <= last; ival += inc) { v.push_back(ival); }
      else
        for (int ival = first; ival >= last; ival += inc) { v.push_back(ival); }
    }
  }

  return v;
}

template <>
std::vector<double>
SelectKey<double>::get_values(KeyValues const &kv)
{
  std::vector<double> v(kv.nvalues);
  for (int i = 0; i < kv.nvalues; ++i) { v[i] = parameter_to_double(kv.values[i]); }
  return v;
}

template <>
std::vector<std::string>
SelectKey<std::string>::get_values(KeyValues const &kv)
{
  std::vector<std::string> v(kv.nvalues);
  for (int i = 0; i < kv.nvalues; ++i) { v[i] = parameter_to_word(kv.values[i]); }
  return v;
}

template <>
bool
SelectKey<int>::check()
{
  bool found{ false };
  for (int i = 0; i < nvalues; ++i)
  {
    if (m_value == valueList[i]) { flag[i] = found = true; }
  }
  return found;
}

template <>
bool
SelectKey<double>::check()
{
  constexpr double tolerance = 1e-4;
  bool found{ false };
  for (int i = 0; i < nvalues; ++i)
  {
    if (std::abs(m_value - valueList[i]) < tolerance) { flag[i] = found = true; }
  }
  return found;
}

template <>
bool
SelectKey<std::string>::check()
{
  bool found{ false };
  for (int i = 0; i < nvalues; ++i)
  {
    if (wildcard_match(m_value, valueList[i])) { flag[i] = found = true; }
  }
  return found;
}

template <>
bool
SelectKey<int>::check_index(int maxValues)
{
  bool found{ false };
  for (int i = 0; i < nvalues; ++i)
  {
    int value = (valueList[i] >= 0) ? valueList[i] : maxValues + valueList[i] + 1;
    if (m_value == value) { flag[i] = found = true; }
  }
  return found;
}

template <>
bool
SelectKey<double>::check_range(double value)
{
  bool found{ false };
  if (nvalues == 2 && value >= valueList[0] && value <= valueList[1]) { flag[0] = flag[1] = found = true; }
  return found;
}

template <>
void
SelectKey<double>::check_range_flag()
{
  if (nvalues == 2 && !flag[0]) { cdo_warning("%s %g to %g not found!", m_description, valueList[0], valueList[1]); }
}

template <>
bool
SelectKey<std::string>::check_date()
{
  bool found{ false };
  auto value = m_value;
  if (value.starts_with(' ')) { value = value.substr(1); }
  for (int i = 0; i < nvalues; ++i)
  {
    if (wildcard_match(value, string_to_upper(valueList[i]) + '*')) { flag[i] = found = true; }
  }
  return found;
}

template <>
bool
SelectKey<std::string>::check_season(int month)
{
  bool found{ false };
  for (int i = 0; i < nvalues; ++i)
  {
    std::array<int, 13> imon{};
    season_to_months(valueList[i], imon);
    if (imon[month]) { flag[i] = found = true; }
  }
  return found;
}
