#ifndef EIGENVALUE_SOLVER_OPTIONS_HPP
#define EIGENVALUE_SOLVER_OPTIONS_HPP

#include <utils/Exceptions.hpp>

#include <iostream>

enum class ESLibrary : int8_t
{
  ES__begin = 0,
  //
  eigen3 = ES__begin,
  slepsc,
  //
  ES__end
};

inline std::string
name(const ESLibrary library)
{
  switch (library) {
  case ESLibrary::eigen3: {
    return "Eigen3";
  }
  case ESLibrary::slepsc: {
    return "SLEPSc";
  }
  case ESLibrary::ES__end: {
  }
  }
  throw UnexpectedError("Eigenvalue solver library name is not defined!");
}

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

template <typename ESEnumType>
inline void
printESEnumListNames(std::ostream& os)
{
  using BaseT = std::underlying_type_t<ESEnumType>;
  for (BaseT enum_value = static_cast<BaseT>(ESEnumType::ES__begin);
       enum_value < static_cast<BaseT>(ESEnumType::ES__end); ++enum_value) {
    os << "  - " << name(ESEnumType{enum_value}) << '\n';
  }
}

class EigenvalueSolverOptions
{
 private:
  ESLibrary m_library = ESLibrary::slepsc;

  double m_epsilon           = 1E-6;
  size_t m_maximum_iteration = 200;

  bool m_verbose = false;

 public:
  static EigenvalueSolverOptions default_options;

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

  ESLibrary&
  library()
  {
    return m_library;
  }

  ESLibrary
  library() const
  {
    return m_library;
  }

  bool&
  verbose()
  {
    return m_verbose;
  };

  bool
  verbose() const
  {
    return m_verbose;
  };

  EigenvalueSolverOptions(const EigenvalueSolverOptions&) = default;
  EigenvalueSolverOptions(EigenvalueSolverOptions&&)      = default;

  EigenvalueSolverOptions()  = default;
  ~EigenvalueSolverOptions() = default;
};

inline EigenvalueSolverOptions EigenvalueSolverOptions::default_options;

#endif   // EIGENVALUE_SOLVER_OPTIONS_HPP