#ifndef PARTITIONER_OPTIONS_HPP
#define PARTITIONER_OPTIONS_HPP

#include <utils/Exceptions.hpp>
#include <utils/pugs_config.hpp>

#include <iostream>

enum class PartitionerLibrary : int8_t
{
  PT__begin = 0,
  //
  parmetis = PT__begin,
  ptscotch,
  //
  PT__end
};

inline std::string
name(const PartitionerLibrary library)
{
  switch (library) {
  case PartitionerLibrary::parmetis: {
    return "ParMETIS";
  }
  case PartitionerLibrary::ptscotch: {
    return "PTScotch";
  }
  case PartitionerLibrary::PT__end: {
  }
  }
  throw UnexpectedError("Linear system library name is not defined!");
}

template <typename PartitionerEnumType>
inline PartitionerEnumType
getPartitionerEnumFromName(const std::string& enum_name)
{
  using BaseT = std::underlying_type_t<PartitionerEnumType>;
  for (BaseT enum_value = static_cast<BaseT>(PartitionerEnumType::PT__begin);
       enum_value < static_cast<BaseT>(PartitionerEnumType::PT__end); ++enum_value) {
    if (name(PartitionerEnumType{enum_value}) == enum_name) {
      return PartitionerEnumType{enum_value};
    }
  }
  throw NormalError(std::string{"could not find '"} + enum_name + "' associate type!");
}

class PartitionerOptions
{
 private:
  PartitionerLibrary m_library = []() {
#if !defined(PUGS_HAS_PARMETIS) && defined(PUGS_HAS_PTSCOTCH)
    return PartitionerLibrary::ptscotch;
#else   // sets parmetis as default if no alternative is available
    return PartitionerLibrary::parmetis;
#endif
  }();

 public:
  static PartitionerOptions default_options;

  friend std::ostream& operator<<(std::ostream& os, const PartitionerOptions& options);

  PartitionerLibrary&
  library()
  {
    return m_library;
  }

  PartitionerLibrary
  library() const
  {
    return m_library;
  }

  PartitionerOptions(const PartitionerOptions&) = default;
  PartitionerOptions(PartitionerOptions&&)      = default;

  PartitionerOptions()  = default;
  ~PartitionerOptions() = default;
};

inline PartitionerOptions PartitionerOptions::default_options;

#endif   // PARTITIONER_OPTIONS_HPP