#ifndef POLYNOMIALBASIS_HPP
#define POLYNOMIALBASIS_HPP

#include <algebra/TinyVector.hpp>
#include <analysis/Polynomial.hpp>
#include <utils/Messenger.hpp>

enum class BasisType
{
  canonical
};

template <size_t N>
class PolynomialBasis
{
 private:
  static_assert((N >= 0), "Number of elements in the basis must be non-negative");
  TinyVector<N + 1, Polynomial<N>> m_elements;
  BasisType m_basis_type;
  PUGS_INLINE
  constexpr PolynomialBasis<N>&
  _buildCanonicalBasis()
  {
    for (size_t i = 0; i <= N; i++) {
      TinyVector<N + 1> coefficients(zero);
      coefficients[i] = 1;
      elements()[i]   = Polynomial<N>(coefficients);
    }
    return *this;
  }

 public:
  PUGS_INLINE
  constexpr size_t
  size() const
  {
    return N + 1;
  }

  PUGS_INLINE
  constexpr size_t
  degree() const
  {
    return N;
  }

  PUGS_INLINE
  constexpr BasisType&
  type()
  {
    return m_basis_type;
  }

  PUGS_INLINE
  std::string_view
  displayType()
  {
    switch (m_basis_type) {
    case BasisType::canonical:
      return "canonical";
    default:
      return "unknown basis type";
    }
  }

  PUGS_INLINE
  constexpr const TinyVector<N + 1, Polynomial<N>>&
  elements() const
  {
    return m_elements;
  }

  PUGS_INLINE
  constexpr TinyVector<N + 1, Polynomial<N>>&
  elements()
  {
    return m_elements;
  }

  PUGS_INLINE
  constexpr PolynomialBasis<N>&
  build(BasisType basis_type)
  {
    type() = basis_type;
    switch (basis_type) {
    case BasisType::canonical: {
      return _buildCanonicalBasis();
      break;
    }
    default:
      throw UnexpectedError("unknown basis type");
    }
  }
  PUGS_INLINE constexpr PolynomialBasis(const TinyVector<N + 1, Polynomial<N>>& elements) noexcept
    : m_elements{elements}
  {}

  PUGS_INLINE
  constexpr PolynomialBasis(TinyVector<N + 1, Polynomial<N>>&& elements) noexcept : m_elements{elements} {}

  PUGS_INLINE
  constexpr PolynomialBasis() noexcept = default;
  ~PolynomialBasis()                   = default;
};
#endif   // POLYNOMIAL_HPP