#ifndef LINE_TRANSFORMATION_HPP
#define LINE_TRANSFORMATION_HPP

#include <algebra/TinyVector.hpp>

template <size_t GivenDimension>
class LineTransformation;

template <>
class LineTransformation<1>
{
 public:
  constexpr static size_t Dimension = 1;

 private:
  double m_jacobian;
  TinyVector<Dimension> m_shift;

 public:
  PUGS_INLINE
  TinyVector<Dimension>
  operator()(const TinyVector<1>& x) const
  {
    return m_jacobian * x + m_shift;
  }

  double
  jacobianDeterminant() const
  {
    return m_jacobian;
  }

  PUGS_INLINE
  LineTransformation(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b)
  {
    m_jacobian = 0.5 * (b[0] - a[0]);
    m_shift    = 0.5 * (a + b);
  }

  ~LineTransformation() = default;
};

template <size_t GivenDimension>
class LineTransformation
{
 public:
  constexpr static size_t Dimension = GivenDimension;

 private:
  TinyVector<Dimension> m_velocity;
  const double m_velocity_norm;
  TinyVector<Dimension> m_shift;

 public:
  PUGS_INLINE
  TinyVector<Dimension>
  operator()(const TinyVector<1>& x) const
  {
    return x[0] * m_velocity + m_shift;
  }

  PUGS_INLINE
  const TinyVector<Dimension>&
  velocity() const
  {
    return m_velocity;
  }

  PUGS_INLINE
  const TinyVector<Dimension>&
  velocity(const TinyVector<1>&) const
  {
    return m_velocity;
  }

  PUGS_INLINE
  double
  velocityNorm() const
  {
    return m_velocity_norm;
  }

  PUGS_INLINE
  double
  velocityNorm(const TinyVector<1>&) const
  {
    return m_velocity_norm;
  }

  PUGS_INLINE
  LineTransformation(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b)
    : m_velocity{0.5 * (b - a)}, m_velocity_norm{l2Norm(m_velocity)}, m_shift{0.5 * (a + b)}
  {
    static_assert(Dimension > 1);
  }

  ~LineTransformation() = default;
};

#endif   // LINE_TRANSFORMATION_HPP