#ifndef OUTPUT_NAMED_ITEM_VALUE_SET_HPP
#define OUTPUT_NAMED_ITEM_VALUE_SET_HPP

#include <ItemValue.hpp>
#include <TinyMatrix.hpp>
#include <TinyVector.hpp>

#include <map>
#include <string>
#include <variant>

template <typename DataType, ItemType item_type>
class NamedItemValue
{
 private:
  std::string m_name;
  ItemValue<const DataType, item_type> m_item_value;

 public:
  constexpr const std::string&
  name() const
  {
    return m_name;
  }

  constexpr const ItemValue<const DataType, item_type>&
  itemValue() const
  {
    return m_item_value;
  }

  NamedItemValue& operator=(const NamedItemValue&) = default;
  NamedItemValue& operator=(NamedItemValue&&) = default;

  template <typename ConnectivityPtr>
  NamedItemValue(
    const std::string& name,
    const ItemValue<DataType, item_type, ConnectivityPtr>& item_value)
    : m_name(name), m_item_value(item_value)
  {
    ;
  }

  NamedItemValue(const std::string& name,
                 const ItemValue<const DataType, item_type>& item_value)
    : m_name(name), m_item_value(item_value)
  {
    ;
  }

  NamedItemValue(const NamedItemValue&) = default;
  NamedItemValue(NamedItemValue&&)      = default;
  ~NamedItemValue()                     = default;
};

class OutputNamedItemValueSet
{
 public:
  using ItemValueVariant = std::variant<NodeValue<const int>,
                                        NodeValue<const long int>,
                                        NodeValue<const double>,
                                        NodeValue<const TinyVector<1, double>>,
                                        NodeValue<const TinyVector<2, double>>,
                                        NodeValue<const TinyVector<3, double>>,

                                        CellValue<const int>,
                                        CellValue<const long int>,
                                        CellValue<const double>,
                                        CellValue<const TinyVector<1, double>>,
                                        CellValue<const TinyVector<2, double>>,
                                        CellValue<const TinyVector<3, double>>>;

 private:
  std::map<std::string, ItemValueVariant> m_name_itemvariant_map;

  template <typename DataType, ItemType item_type>
  PUGS_FORCEINLINE constexpr void
  _doInsert(const NamedItemValue<DataType, item_type>& named_itemvalue)
  {
    if (m_name_itemvariant_map.find(named_itemvalue.name()) ==
        m_name_itemvariant_map.end()) {
      m_name_itemvariant_map[named_itemvalue.name()] =
        named_itemvalue.itemValue();
    }
  }

  template <typename DataType, ItemType item_type, typename... Args>
  PUGS_FORCEINLINE constexpr void
  _unpackVariadicInput(
    const NamedItemValue<DataType, item_type>& named_itemvalue,
    Args&&... args)
  {
    _doInsert(named_itemvalue);
    if constexpr (sizeof...(args) > 0) {
      this->_unpackVariadicInput(std::forward<Args>(args)...);
    }
  }

 public:
  auto
  begin() const
  {
    return m_name_itemvariant_map.begin();
  }

  auto
  end() const
  {
    return m_name_itemvariant_map.end();
  }

  template <typename... DataType, ItemType... item_type>
  OutputNamedItemValueSet(
    NamedItemValue<DataType, item_type>... named_itemvalue)
  {
    _unpackVariadicInput(named_itemvalue...);
  }

  OutputNamedItemValueSet(const OutputNamedItemValueSet&) = default;
  OutputNamedItemValueSet()                               = default;
  ~OutputNamedItemValueSet()                              = default;
};

#endif   // OUTPUT_NAMED_ITEM_VALUE_SET_HPP