#include "row.hpp"

/* =========================
   CONSTRUCTORS
   ========================= */

template<typename T>
row<T>::row() : p(nullptr), n(0), owns(false)
{
}

template<typename T>
row<T>::row(T* data, uint32_t size, bool copy_data) : p(nullptr), n(size), owns(copy_data)
{
    if (n == 0)
        return;

    if (copy_data)
    {
        p = new T[n];
        for (uint32_t i = 0; i < n; ++i)
            p[i] = data[i];
    }
    else
    {
        p = data;
    }
}

template<typename T>
row<T>::row(const row& other) : p(nullptr), n(other.n), owns(true)
{
    if (n == 0)
        return;

    p = new T[n];
    for (uint32_t i = 0; i < n; ++i)
        p[i] = other.p[i];
}

template<typename T>
row<T>::row(row&& other) noexcept : p(other.p), n(other.n), owns(other.owns)
{
    other.p = nullptr;
    other.n = 0;
    other.owns = false;
}

template<typename T>
row<T>& row<T>::operator=(const row& other)
{
    if (this == &other)
        return *this;

    if (owns)
        delete[] p;

    n = other.n;
    owns = true;
    p = nullptr;

    if (n > 0)
    {
        p = new T[n];
        for (uint32_t i = 0; i < n; ++i)
            p[i] = other.p[i];
    }

    return *this;
}

template<typename T>
row<T>& row<T>::operator=(row&& other) noexcept
{
    if (this == &other)
        return *this;

    if (owns)
        delete[] p;

    p = other.p;
    n = other.n;
    owns = other.owns;

    other.p = nullptr;
    other.n = 0;
    other.owns = false;

    return *this;
}

template<typename T>
row<T>::~row()
{
    if (owns)
        delete[] p;
}

/* =========================
   MEMBER FUNCTIONS
   ========================= */

template<typename T>
uint32_t row<T>::size() const
{
    return n;
}

template<typename T>
T& row<T>::operator[](uint32_t index)
{
    if (index >= n)
    {
        std::cerr << "Index out of bounds\n";
        std::abort();
    }
    return p[index];
}

template<typename T>
const T& row<T>::operator[](uint32_t index) const
{
    if (index >= n)
    {
        std::cerr << "Index out of bounds\n";
        std::abort();
    }
    return p[index];
}

template<typename T>
T& row<T>::at(uint32_t index)
{
    if (index >= n)
    {
        std::cerr << "Index out of bounds in at()\n";
        std::abort();
    }
    return p[index];
}

template<typename T>
const T& row<T>::at(uint32_t index) const
{
    if (index >= n)
    {
        std::cerr << "Index out of bounds in at()\n";
        std::abort();
    }
    return p[index];
}

template<typename T>
T* row<T>::data()
{
    return p;
}

template<typename T>
const T* row<T>::data() const
{
    return p;
}

/* =========================
   STREAM OPERATORS
   ========================= */

template<typename T>
std::ostream& operator<<(std::ostream& os, const row<T>& obj)
{
    for (uint32_t i = 0; i < obj.n; ++i)
        os << obj.p[i] << " ";
    return os;
}

template<typename T>
std::istream& operator>>(std::istream& is, row<T>& obj)
{
    for (uint32_t i = 0; i < obj.n; ++i)
        is >> obj.p[i];
    return is;
}

/* =========================
   EXPLICIT INSTANTIATION
   ========================= */

template class row<int>;
template class row<long int>;
template class row<double>;
template class row<float>;
template class row<uint8_t>;

template std::ostream& operator<<(std::ostream&, const row<int>&);
template std::ostream& operator<<(std::ostream&, const row<long int>&);
template std::ostream& operator<<(std::ostream&, const row<float>&);
template std::ostream& operator<<(std::ostream&, const row<double>&);
template std::ostream& operator<<(std::ostream&, const row<uint8_t>&);

template std::istream& operator>>(std::istream&, row<int>&);
template std::istream& operator>>(std::istream&, row<long int>&);
template std::istream& operator>>(std::istream&, row<float>&);
template std::istream& operator>>(std::istream&, row<double>&);
template std::istream& operator>>(std::istream&, row<uint8_t>&);