//#####################################################################
// 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_FACE_ARRAY
//#####################################################################
#ifndef __REGION_FACE_ARRAY__
#define __REGION_FACE_ARRAY__

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

template<int d>
struct REGION_FACE_INDEX
{
    REGION_ID r;
    FACE_INDEX<d> f;
};

template<class T,class ID> class REGION_ARRAY;

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

    ~REGION_ARRAY()
    {
        for(auto& r:u) r.array.Set(0,0);
    }

    T& operator()(const REGION_FACE_INDEX<d>& ri) {return u(ri.r)(ri.f);}
    const T& operator()(const REGION_FACE_INDEX<d>& ri) const {return u(ri.r)(ri.f);}

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

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

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

    T* Resize_Helper(REGION_ID r,T* p,const RANGE<IV>& domain)
    {
        auto size=ARRAY<T,FACE_INDEX<d> >::Calculate_Raw_Size(domain);
        ARRAY<T,FACE_INDEX<d> > new_array;
        new_array.array.Set(p,size);
        new_array.Initialize_Using_Array(domain);
        p+=new_array.array.m;
        u(r).Exchange(new_array);
        new_array.array.Set(0,0);
        return p;
    }

    void Resize(const ARRAY<RANGE<IV>,REGION_ID>& region_ranges)
    {
        u.Resize(region_ranges.m);
        REGION_FACE_FLAT_ID space_needed(0);
        for(auto& r:region_ranges)
            space_needed+=ARRAY<T,FACE_INDEX<d> >::Calculate_Raw_Size(r);
        raw.Resize(space_needed);
        T* p=raw.Get_Array_Pointer();
        for(REGION_ID r(0);r<u.m;r++)
            p=Resize_Helper(r,p,region_ranges(r));
    }

    template<class U>
    void Resize(const REGION_ARRAY<U,FACE_INDEX<d> >& a)
    {
        raw.Resize(a.raw.m);
        u.Resize(a.u.m);
        T* p=raw.Get_Array_Pointer();
        for(REGION_ID r(0);r<u.m;r++)
            p=Resize_Helper(r,p,a.u(r).domain);
    }

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

    bool Valid_Index(const REGION_FACE_INDEX<d>& ri) const
    {
        return u.Valid_Index(ri.r) && u(ri.r).Valid_Index(ri.f);
    }
    
//#####################################################################
};
}
#endif
