//#####################################################################
// Copyright 2007-2008, Geoffrey Irving, Eftychios Sifakis.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class ARRAY_VIEW
//#####################################################################
#ifndef __ARRAYS_ND_VIEW__
#define __ARRAYS_ND_VIEW__

#include <Core/Arrays/ARRAY_VIEW.h>
#include <Core/Arrays/ARRAYS_ND_BASE.h>
#include <Core/Math_Tools/exchange.h>
#include <Core/Utilities/EXCEPTIONS.h>
#include <Core/Utilities/TYPE_UTILITIES.h>
#include <Core/Vectors/VECTOR.h>
namespace PhysBAM{

template<class T,int d>
class ARRAY_VIEW<T,VECTOR<int,d> >:public ARRAYS_ND_BASE<typename remove_const<T>::type,VECTOR<int,d> >
{
    typedef VECTOR<int,d> IV;
    struct UNUSABLE{};
    template<class S> struct COPY_CONST{typedef typename conditional<is_const<T>::value,typename add_const<S>::type,S>::type TYPE;};
public:
    typedef typename remove_const<T>::type ELEMENT;typedef IV INDEX;
    typedef ARRAYS_ND_BASE<ELEMENT,VECTOR<int,d> > BASE;
    typedef typename conditional<is_const<T>::value,ELEMENT,UNUSABLE>::type U;
    typedef T& RESULT_TYPE;

    using BASE::domain;using BASE::array;
private:
    friend class ARRAY_VIEW<typename conditional<is_const<T>::value,ELEMENT,const ELEMENT>::type,IV>;

    using BASE::Calculate_Acceleration_Constants;

public:

    ARRAY_VIEW() = default;

    ARRAY_VIEW(T* raw_data,const RANGE<IV>& domain)
        :BASE(domain)
    {array.Set((ELEMENT*)raw_data,domain.Size());}

    ARRAY_VIEW(const ARRAY_VIEW& a) = default;

    ARRAY_VIEW(const ARRAY_VIEW<U,IV>& a)
        :ARRAY_VIEW((T*)a.Get_Array_Pointer(),a.domain)
    {}

    ARRAY_VIEW(const ARRAYS_ND_BASE<U,IV>& a)
        :ARRAY_VIEW((T*)a.Get_Array_Pointer(),a.domain)
    {}

    ARRAY_VIEW(const ARRAYS_ND_BASE<T,IV>& a)
        :ARRAY_VIEW((T*)a.Get_Array_Pointer(),a.domain)
    {}

    template<class T_ARRAY1>
    ARRAY_VIEW& operator=(const T_ARRAY1& source)
    {assert(Equal_Dimensions(*this,source));array=source.array;return *this;}

    void Exchange(ARRAY_VIEW& other)
    {static_assert(!is_const<T>::value);this->BASE::Exchange(other);} // make ARRAY_VIEW<const T> equivalent to const ARRAY_VIEW<const T>

    static void Exchange(ARRAY_VIEW& array0,ARRAY_VIEW& array1)
    {static_assert(!is_const<T>::value);array0.Exchange(array1);}

    void Set(ELEMENT* raw_data,const RANGE<IV>& domain)
    {
        if(raw_data)
        {
            array.Set(raw_data,domain.Size());
            Calculate_Acceleration_Constants(domain);
        }
        else
        {
            array.Set(nullptr,0);
            this->domain=RANGE<IV>::Empty_Box();
            this->stride=IV();
            this->offset=0;
        }
    }

    void Set(const ARRAY_VIEW<ELEMENT,IV>& array)
    {Set((ELEMENT*)array.Get_Array_Pointer(),array.domain);}

    template<class T_ARRAY>
    void Set(T_ARRAY& array,enable_if_t<is_same<ELEMENT,typename T_ARRAY::ELEMENT>::value && !IS_ARRAY_VIEW<T_ARRAY>::value,UNUSABLE> unusable=UNUSABLE())
    {Set(array.Get_Array_Pointer(),array.Domain_Indices());}

    template<class T_ARRAY>
    void Set(T_ARRAY array,enable_if_t<is_same<ELEMENT,typename T_ARRAY::ELEMENT>::value && IS_ARRAY_VIEW<T_ARRAY>::value,UNUSABLE> unusable=UNUSABLE())
    {Set(array.Get_Array_Pointer(),array.Domain_Indices());}

    static bool Same_Array(const ARRAY_VIEW& array0,const ARRAY_VIEW& array1)
    {return array0.Get_Array_Pointer()==array1.Get_Array_Pointer();}

//#####################################################################
};
}
#endif
