#ifndef __AMSCPPNARRAY_IMPL_HPP__
#define __AMSCPPNARRAY_IMPL_HPP__

namespace ams
{
namespace narray
{

template<typename T> narray<T>::narray()
{
    //printf("debug: constructor called for %p.\n",this);

    length = 0;
    data = NULL;

    return;
}

template<typename T> int narray<T>::resize(const narray_size_t newlength)
{
    int ret = narray_failure;
    narray_size_t I;
    T *newdata = NULL;

    if(newlength<=0)
    {
        if(data!=NULL) {delete[] data; data=NULL;}
        length = 0;
        ret = narray_success;
        return ret;
    }

    newdata = new(std::nothrow) T[newlength];
    if(newdata==NULL)
    {
        ret = narray_failure;
        return ret;
    }

    for(I=0;I<length&I<newlength;I++)
    {
        newdata[I] = data[I];
    }
    for(I=length;I<newlength;I++)
    {
        newdata[I] = T();
    }

    if(data!=NULL) {delete[] data; data=NULL;}
    data = newdata;
    length = newlength;
    ret = narray_success;

    return ret;
}

template<typename T> narray<T>::narray(const narray<T>& other)
{
    narray_size_t I;
    int res;
    length = 0;
    data = NULL;

    //printf("debug: copy constructor called for\n\t%p\n\t%p\n", this, &other);

    if(this!=&other)
    {
        res = this->resize(other.length);
        if(res!=narray_success)
        {
            return;
        }
        else
        {
            for(I=0;I<length && I<other.length;I++)
            {
                data[I] = other.data[I];
            }
        }
    }
}

template<typename T> narray<T>::narray(narray<T>&& other) noexcept
{
    //this is a move operator
    //I still need to understand when these are called.

    //printf("debug: move constructor called for\n\t%p\n\t%p\n", this, &other);

    //steal the resources from other and reset other to prevent double deletion
    length = other.length;
    data = other.data;

    other.length = 0;
    other.data = NULL;

    return;
}

template<typename T> narray<T>& narray<T>::operator=(narray<T>&& other) noexcept
{

    //printf("debug: move= called for\n\t%p\n\t%p\n", this, &other);

    if(this!=&other)
    {
        if(data!=NULL) {delete[] data; data=NULL;}
        
        length = other.length;
        data = other.data;

        other.length = 0;
        other.data = NULL;
    }
    return *this;
}

template<typename T> narray<T>& narray<T>::operator=(const narray<T>& other)
{
    narray_size_t I;
    int res;

    //printf("debug: operator= called for\n\t%p\n\t%p\n", this, &other);

    if(this!=&other)
    {
        res = this->resize(other.length);
        if(res!=narray_success)
        {
            return *this;
        }
        else
        {
            for(I=0;I<length && I<other.length;I++)
            {
                data[I] = other.data[I];
            }
        }
    }
    return *this;
}

template<typename T> narray<T>& narray<T>::operator=(const std::vector<T>& other)
{
    narray_size_t I;
    int res;

    if(this!=&other)
    {
        res = this->resize(other.size());
        if(res!=narray_success)
        {
            return *this;
        }
        else
        {
            for(I=0;I<length && I<other.size();I++)
            {
                data[I] = other[I];
            }
        }
    }
    return *this;
}

template<typename T> narray<T>::narray(const std::initializer_list<T> initlist)
{
    narray_size_t I;
    int res;
    length = 0;
    data = NULL;

    res = this->resize(initlist.size());
    if(res!=narray_success)
    {
        return;
    }
    else
    {
        I = 0;
        for(T elem : initlist)
        {
            data[I] = elem;
            if(I<length) I++;
        }
    }
}

template<typename T> narray<T>& narray<T>::operator=(const std::initializer_list<T> initlist)
{
    narray_size_t I;
    int res;

    res = this->resize(initlist.size());
    if(res!=narray_success)
    {
        this->length = 0;
        if(this->data != NULL) {delete[] this->data; this->data=NULL;}
        return *this;
    }
    else
    {
        I = 0;
        for(T elem : initlist)
        {
            data[I] = elem;
            if(I<length) I++;
        }
    }

    return *this;
}

template<typename T> narray<T>::~narray()
{
    //printf("debug: destructor called for %p\n",this);
    length = 0;
    if(data!=NULL) {delete[] data; data=NULL;}
    return;
}

template<typename T> T& narray<T>::operator[](const narray_size_t ind)
{
    return data[ind];
}

template<typename T> const T& narray<T>::operator[](const narray_size_t ind) const
{
    return data[ind];
}

template<typename T> T& narray<T>::at(const narray_size_t ind)
{
    return data[ind];
}

template<typename T> const T& narray<T>::at(const narray_size_t ind) const
{
    return data[ind];
}

template<typename T> void narray<T>::clear()
{
    this->resize(0);
    return;
}

template<typename T> void narray_setall_tf(narray<T> *q, const T& val, int threadnum, int nthreads)
{
    int I0,I1,Is,I;

    Is = q->length/nthreads;
    I0 = Is*threadnum;
    I1 = (threadnum>=(nthreads-1))? q->length: Is*(threadnum+1);
    for(I=I0;I<I1;I++)
    {
        q->data[I] = val;
    }

    return;
}

template<typename T> void narray<T>::setall(const T& val)
{
    narray_size_t I;
    int J;
    int nthreads;
    std::vector<std::thread*> threads;

    if(length<narray_thread_sz)
    {
        for(I=0;I<length;I++) data[I] = val;
    }
    else
    {
        //threaded operation
        nthreads = std::thread::hardware_concurrency();
        if(nthreads<1) nthreads=1;
        if(nthreads>narray_max_threads) nthreads = narray_max_threads;
        threads.resize(nthreads);
        for(J=0;J<nthreads;J++)
        {
            threads[J] = new(std::nothrow) std::thread(
                &narray_setall_tf<T>,this,val,J,nthreads
            );
        }
        for(J=0;J<nthreads;J++)
        {
            if(threads[J]!=NULL)
            { 
                threads[J]->join(); 
                delete threads[J]; 
                threads[J]= NULL;
            }
        }
    }
    return;
}

template<typename T> const narray_size_t narray<T>::size() const
{
    return length;
}

}; //end namespace narray
}; //end namespace ams

#endif