//#####################################################################
// Copyright 2017, Ounan Ding, Craig Schroeder.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class FACE_RANGE_ITERATOR
//#####################################################################
#ifndef __FACE_RANGE_ITERATOR__
#define __FACE_RANGE_ITERATOR__

#include <Core/Grids/FACE_INDEX.h>
#include <Core/Math_Tools/RANGE.h>
#include <Core/Utilities/PHYSBAM_ATTRIBUTE.h>

namespace PhysBAM{

// 
//   g   g   g   f   f   f   f   f   g   g   g
// g X e X e X d X c X c X c X c X d X e X e X g
//   e   e   e   c   c   c   c   c   e   e   e
// g X e X e X d X c X c X c X c X d X e X e X g
//   e   e   e   c   c   c   c   c   e   e   e
// g X e X e X d X c X c X c X c X d X e X e X g
//   d   d   d   b   b   b   b   b   d   d   d
// f X c X c X b O a O a O a O a O b X c X c X f
//   c   c   c   a   a   a   a   a   c   c   c
// f X c X c X b O a O a O a O a O b X c X c X f
//   c   c   c   a   a   a   a   a   c   c   c
// f X c X c X b O a O a O a O a O b X c X c X f
//   c   c   c   a   a   a   a   a   c   c   c
// f X c X c X b O a O a O a O a O b X c X c X f
//   c   c   c   a   a   a   a   a   c   c   c
// f X c X c X b O a O a O a O a O b X c X c X f
//   d   d   d   b   b   b   b   b   d   d   d
// g X e X e X d X c X c X c X c X d X e X e X g
//   e   e   e   c   c   c   c   c   e   e   e
// g X e X e X d X c X c X c X c X d X e X e X g
//   e   e   e   c   c   c   c   c   e   e   e
// g X e X e X d X c X c X c X c X d X e X e X g
//   g   g   g   f   f   f   f   f   g   g   g
// 
// Flags: (side=-1)
//   (default)                                      a+b+c+d+e+f+g
//   ghost                                       *  b+c+d+e+f+g
// 
//   ghost|skip_inner                            *  c+d+e+f+g
//   ghost|skip_outer                            *  b+c+d+e
//   ghost|skip_inner|skip_outer                 *  c+d+e
//   skip_outer                                     a+b+c+d+e
// 
//   ghost|duplicate_corners                        b+c+d+d+e+e+f+g+g
//   ghost|duplicate_corners|skip_inner             c+d+e+e+f+g+g
//   ghost|duplicate_corners|skip_outer             b+c+d+d+e+e
//   ghost|duplicate_corners|skip_inner|skip_outer  c+d+e+e
// 
//   ghost|omit_corners                        b+c+d+f
//   ghost|omit_corners|skip_inner             c+d+f
//   ghost|omit_corners|skip_outer             b+c+d
//   ghost|omit_corners|skip_inner|skip_outer  c+d
// 
// For combinations above with "*" these flags can be added:
//   delay_corners         Corners are normally covered with the first
//                         side that makes sense.  This flag delays the
//                         corners until the last side that makes sense.
//                         The faces covered does not change.
// 
// Other flags:
//   reverse               Reverses the order in which faces are visited.
//                         Note that with this flag you must use Prev() and
//                         Prev_Valid().
//   side_mask             Allows you to iterate over a subset of axes.
//                         When this flag is set, the side input integer is
//                         a bitmask.  When set, the iterator always behaves
//                         as though multiple axes are being iterated.
//   axis_mask             Allows you to iterate over a subset of axes.
//                         When this flag is set, the axis input integer is
//                         a bitmask.

enum class RF
{
    none=0,
    ghost=1,
    delay_corners=2,
    duplicate_corners=4,
    skip_inner=8,
    skip_outer=0x10,
    reverse=0x40,
    axis_mask=0x100,
    side_mask=0x200,
    omit_corners=0x400
};
inline RF operator|(RF a,RF b){return RF((int)a|(int)b);}
inline RF operator&(RF a,RF b){return RF((int)a&(int)b);}
inline RF operator^(RF a,RF b){return RF((int)a^(int)b);}
inline RF& operator|=(RF& a,RF b){return a=a|b;}
inline RF& operator&=(RF& a,RF b){return a=a&b;}
inline RF& operator^=(RF& a,RF b){return a=a^b;}
inline RF operator~(RF a){return RF(~(int)a);}
inline bool operator!(RF a){return !(int)a;}
inline bool any(RF a){return (int)a;}

template<int d>
class FACE_RANGE_ITERATOR
{
    typedef VECTOR<int,d> IV;

protected:
    IV vecs[4];
    long inst[2*d];

    IV current[2];
public:
    int axis_mask;
    int side_mask;
    int side;
    FACE_INDEX<d> face;

    FACE_RANGE_ITERATOR()=default;
    FACE_RANGE_ITERATOR(const RANGE<IV>& outer,const RANGE<IV>& inner,
        RF flags=RF::none,int side_input=-1,int axis=-1);
    FACE_RANGE_ITERATOR(const RANGE<IV>& range,int outer_ghost,
        int inner_ghost,RF flags=RF::none,int side_input=-1,int axis=-1);
    FACE_RANGE_ITERATOR(const IV& counts,int outer_ghost,int inner_ghost,
        RF flags=RF::none,int side_input=-1,int axis=-1);
    explicit FACE_RANGE_ITERATOR(const RANGE<IV>& range,
        RF flags=RF::none,int axis=-1);
    FACE_RANGE_ITERATOR(const IV& counts,int outer_ghost,
        RF flags=RF::none,int axis=-1);
    ~FACE_RANGE_ITERATOR()=default;

    void Next() PHYSBAM_ALWAYS_INLINE
    {face.index(d-1)++;if(!Valid()) Next_Helper();}

    bool Valid() const PHYSBAM_ALWAYS_INLINE
    {return face.index(d-1)<current[1](d-1);}

    void Prev() PHYSBAM_ALWAYS_INLINE
    {face.index(d-1)--;if(!Prev_Valid()) Prev_Helper();}

    bool Prev_Valid() const PHYSBAM_ALWAYS_INLINE
    {return face.index(d-1)>=current[0](d-1);}

    void Set_Range(const RANGE<IV>& outer,const RANGE<IV>& inner);
    void Set_Range(const RANGE<IV>& range,int outer_ghost,int inner_ghost);
    void Set_Range(const IV& counts,int outer_ghost,int inner_ghost);
    void Initialize(RF flags,int side_input,int axis);

    void Next_Axis_Side();
    void Prev_Axis_Side();
    RANGE<IV> Current_Region() const
    {return {current[0],current[1]};}

private:
    void Next_Helper();
    void Prev_Helper();
    void Fill_Current();
    void Encode(RF flags,int side_input,int axis);
    void Reset(RF flags);
//#####################################################################
};
}
#endif
