#ifndef ARRAY_HPP
#define ARRAY_HPP

#include <PastisAssert.hpp>
#include <Kokkos_Core.hpp>

template <typename DataType>
class Array
{
 private:
  Kokkos::View<DataType*> m_values;

  // Allows const version to access our data
  friend Array<std::add_const_t<DataType>>;

 public:
  KOKKOS_INLINE_FUNCTION
  size_t size() const
  {
    return m_values.extent(0);
  }

  KOKKOS_INLINE_FUNCTION
  DataType& operator[](const size_t& i) const
  {
    Assert(i<m_values.extent(0));
    return m_values[i];
  }

  KOKKOS_INLINE_FUNCTION
  Array(const size_t& size)
      : m_values("anonymous", size)
  {
    static_assert(not std::is_const<DataType>(),
                  "Cannot allocate Array of const data: only view is supported");
  }

  template <typename DataType2>
  KOKKOS_INLINE_FUNCTION
  Array& operator=(const Array<DataType2>& array)
  {
    // ensures that DataType is the same as source DataType2
    static_assert(std::is_same<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>(),
                  "Cannot assign Array of different type");
    // ensures that const is not lost through copy
    static_assert(((std::is_const<DataType2>() and std::is_const<DataType>())
                   or not std::is_const<DataType2>()),
                  "Cannot assign Array of const to  Array of non-const");
    m_values = array.m_values;
    return *this;
  }

  KOKKOS_INLINE_FUNCTION
  Array& operator=(const Array&) = default;

  KOKKOS_INLINE_FUNCTION
  Array& operator=(Array&&) = default;

  KOKKOS_INLINE_FUNCTION
  Array() = default;

  template <typename DataType2>
  KOKKOS_INLINE_FUNCTION
  Array(const Array<DataType2>& array)
  {
    this->operator=(array);
  }

  KOKKOS_INLINE_FUNCTION
  Array(Array&&) = default;

  KOKKOS_INLINE_FUNCTION
  Array(const Array&) = default;

  KOKKOS_INLINE_FUNCTION
  ~Array() = default;
};

#endif // ARRAY_HPP