//#####################################################################
// Copyright 2006-2007, Geoffrey Irving, Tamar Shinar, Andrew Selle, Eftychios Sifakis.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class INDIRECT_ARRAY
//#####################################################################
#ifndef __INDIRECT_ARRAY__
#define __INDIRECT_ARRAY__

#include <Core/Arrays/ARRAY.h>
#include <Core/Arrays/ARRAY_BASE.h>
#include <Core/Arrays/SIMPLE_ITERATOR.h>
#include <Core/Log/DEBUG_UTILITIES.h>
#include <Core/Utilities/TYPE_UTILITIES.h>
#include <Core/Vectors/VECTOR_FORWARD.h>
namespace PhysBAM{

template<class T_ARRAY,class T_INDICES> struct IS_ARRAY<INDIRECT_ARRAY<T_ARRAY,T_INDICES> > {static const bool value=true;};
template<class T_ARRAY,class T_INDICES> struct IS_ARRAY_VIEW<INDIRECT_ARRAY<T_ARRAY,T_INDICES> > {static const bool value=true;};

template<class T_ARRAY,class T_INDICES> // T_INDICES=ARRAY<int>&
class INDIRECT_ARRAY:public ARRAY_BASE<typename T_ARRAY::ELEMENT,INDIRECT_ARRAY<T_ARRAY,T_INDICES>,typename remove_reference<T_INDICES>::type::INDEX>
{
    typedef typename remove_reference<T_INDICES>::type T_INDICES_NO_REFERENCE;
    typedef typename conditional<is_reference<T_INDICES>::value,const T_INDICES_NO_REFERENCE&,const T_INDICES_NO_REFERENCE>::type CONST_T_INDICES;
    static_assert(is_same<typename T_ARRAY::INDEX,typename T_INDICES_NO_REFERENCE::ELEMENT>::value);
//    static_assert((!is_const<T_INDICES_NO_REFERENCE>::value));
    typedef typename T_ARRAY::ELEMENT T;typedef typename T_INDICES_NO_REFERENCE::INDEX ID;
    typedef ARRAY_BASE<T,INDIRECT_ARRAY<T_ARRAY,T_INDICES>,ID> BASE;
    struct UNUSABLE{};
    typedef typename conditional<IS_ARRAY_VIEW<T_ARRAY>::value,T_ARRAY,T_ARRAY&>::type T_ARRAY_VIEW;
public:
    static constexpr int fixed_m=T_INDICES_NO_REFERENCE::fixed_m;
    typedef T ELEMENT;typedef ID INDEX;
    typedef typename T_ARRAY::RESULT_TYPE RESULT_TYPE;
    typedef typename T_ARRAY::CONST_RESULT_TYPE CONST_RESULT_TYPE;
    typedef SIMPLE_ITERATOR<const INDIRECT_ARRAY> const_iterator;
    typedef std::conditional_t<std::is_const_v<std::remove_reference_t<RESULT_TYPE>>,const_iterator,SIMPLE_ITERATOR<INDIRECT_ARRAY>> iterator;

    
    T_ARRAY_VIEW array;
    CONST_T_INDICES indices;

    INDIRECT_ARRAY(T_ARRAY_VIEW array,typename add_lvalue_reference<CONST_T_INDICES>::type indices)
        :array(array),indices(indices)
    {
    }

    INDIRECT_ARRAY(const INDIRECT_ARRAY<typename remove_const<T_ARRAY>::type,T_INDICES>& indirect)
        :array(indirect.array),indices(indirect.indices)
    {}

    ID Size() const
    {return indices.Size();}

    RESULT_TYPE operator()(const ID i)
    {return array(indices(i));}

    CONST_RESULT_TYPE operator()(const ID i) const
    {return array(indices(i));}

    INDIRECT_ARRAY& operator=(const INDIRECT_ARRAY& source)
    {return BASE::operator=(source);}

    template<class T_OTHER_ARRAY>
    INDIRECT_ARRAY& operator=(const T_OTHER_ARRAY& source)
    {return BASE::operator=(source);}

    iterator begin()
    {return iterator(*this,ID());}

    const_iterator begin() const
    {return const_iterator(*this,ID());}

    iterator end()
    {return iterator(*this,Size());}

    const_iterator end() const
    {return const_iterator(*this,Size());}
//#####################################################################
};
}
#endif
