//#####################################################################
// Copyright 2020, Craig Schroeder.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class REGION_ARRAY
//#####################################################################
#ifndef __REGION_ARRAY__
#define __REGION_ARRAY__

#include <Core/Arrays/ARRAY.h>
#include <Core/Arrays/ARRAYS_ND_VIEW.h>
#include <Hybrid_Methods/Regions/REGION_ID.h>
namespace PhysBAM{

template<int d>
struct REGION_INDEX
{
    REGION_ID r;
    VECTOR<int,d> i;

    template<class RW> void Read(UNTYPED_ISTREAM<RW>& input);
    template<class RW> void Write(UNTYPED_OSTREAM<RW>& output) const;
};

template<class T,class ID> class REGION_ARRAY;

template<class T,int d>
class REGION_ARRAY<T,VECTOR<int,d> >
{
public:
    typedef VECTOR<int,d> IV;
    ARRAY<ARRAY_VIEW<T,IV>,REGION_ID> u;
    ARRAY<T,REGION_FLAT_ID> raw;

    REGION_ARRAY()=default;
    REGION_ARRAY(const REGION_ARRAY& w)
    {
        Resize(w);
        raw=w.raw.Array_View({},raw.m);
    }
    REGION_ARRAY(REGION_ARRAY&& w)
        :u(std::move(w.u)),raw(std::move(w.raw))
    {
    }

    ~REGION_ARRAY(){}

    REGION_ARRAY& operator=(const REGION_ARRAY& w)
    {
        Resize(w);
        raw=w.raw.Array_View({},raw.m);
        return *this;
    }

    REGION_ARRAY& operator=(REGION_ARRAY&& w)
    {
        u=std::move(w.u);
        raw=std::move(w.raw);
        return *this;
    }
    
    T& operator()(const REGION_INDEX<d>& ri) {return u(ri.r)(ri.i);}
    const T& operator()(const REGION_INDEX<d>& ri) const {return u(ri.r)(ri.i);}

    T& operator()(REGION_FLAT_ID i) {return raw(i);}
    const T& operator()(REGION_FLAT_ID i) const {return raw(i);}

    REGION_FLAT_ID Flat_Index(const REGION_INDEX<d>& ri) const
    {return REGION_FLAT_ID(&(*this)(ri)-raw.Get_Array_Pointer());}

    REGION_FLAT_ID Flat_Size() const
    {return raw.m;}

    void Resize(const ARRAY<RANGE<IV>,REGION_ID>& region_ranges)
    {
        u.Resize(region_ranges.m);
        REGION_FLAT_ID space_needed(0);
        for(auto& r:region_ranges) space_needed+=r.Size();
        raw.Resize(space_needed);
        REGION_FLAT_ID next_offset(0);
        for(REGION_ID r(0);r<u.m;r++)
        {
            u(r).Set(&raw(next_offset),region_ranges(r));
            next_offset+=region_ranges(r).Size();
        }
    }

    template<class U>
    void Resize(const REGION_ARRAY<U,IV>& a)
    {
        raw.Resize(a.raw.m);
        u.Resize(a.u.m);
        REGION_FLAT_ID next_offset(0);
        for(REGION_ID r(0);r<u.m;r++)
        {
            u(r).Set(&raw(next_offset),a.u(r).domain);
            next_offset+=a.u(r).domain.Size();
        }
    }

    void Exchange(REGION_ARRAY& a)
    {
        u.Exchange(a.u);
        raw.Exchange(a.raw);
    }

    bool Valid_Index(const REGION_INDEX<d>& ri) const
    {
        return u.Valid_Index(ri.r) && u(ri.r).Valid_Index(ri.i);
    }
    
//#####################################################################
};
#define REGION_ARRAY_COMMON(T,d,E)                                      \
    E template REGION_ARRAY<T,VECTOR<int,d> >::~REGION_ARRAY();         \
    E template void REGION_ARRAY<T,VECTOR<int,d> >::Resize(ARRAY<RANGE<VECTOR<int,d> >,REGION_ID> const&); \
    E template void REGION_ARRAY<VECTOR<T,d>,VECTOR<int,d> >::Resize<VECTOR<T,d> >(REGION_ARRAY<VECTOR<T,d>,VECTOR<int,d> > const&); \
    E template void REGION_ARRAY<VECTOR<T,d>,VECTOR<int,d> >::Resize<T>(REGION_ARRAY<T,VECTOR<int,d> > const&); \
    COMMON_ARRAY(ARRAY<ARRAY_VIEW<VECTOR<T COMMA_ARRAY d> COMMA_ARRAY VECTOR<int COMMA_ARRAY d>> COMMA_ARRAY REGION_ID>,int,E) \
    COMMON_ARRAY(ARRAY<ARRAY_VIEW<T COMMA_ARRAY VECTOR<int COMMA_ARRAY d>> COMMA_ARRAY REGION_ID>,int,E)
#define REGION_ARRAY_COMMON_DIM(T,E)            \
    REGION_ARRAY_COMMON(T,1,E);                 \
    REGION_ARRAY_COMMON(T,2,E);                 \
    REGION_ARRAY_COMMON(T,3,E);                 \
    COMMON_ARRAY_VECTOR(T,REGION_ID,E);         \
    COMMON_ARRAY_VECTOR(T,REGION_FLAT_ID,E);
REGION_ARRAY_COMMON_DIM(bool,extern);
REGION_ARRAY_COMMON_DIM(int,extern);
REGION_ARRAY_COMMON_DIM(float,extern);
REGION_ARRAY_COMMON_DIM(double,extern);
COMMON_ARRAY(REGION_ID,int,extern);
COMMON_ARRAY(REGION_FLAT_ID,int,extern);
COMMON_ARRAY(REGION_INDEX<1>,int,extern);
COMMON_ARRAY(REGION_INDEX<2>,int,extern);
COMMON_ARRAY(REGION_INDEX<3>,int,extern);
}
#endif
