//#####################################################################
// 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 RANGE_ITERATOR
//#####################################################################
#ifndef __RANGE_ITERATOR__
#define __RANGE_ITERATOR__

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

namespace PhysBAM{

// c c c b b b b b c c c
// c c c b b b b b c c c
// c c c b b b b b c c c
// b b b a a a a a b b b
// b b b a a a a a b b b
// b b b a a a a a b b b
// b b b a a a a a b b b
// b b b a a a a a b b b
// c c c b b b b b c c c
// c c c b b b b b c c c
// c c c b b b b b c c c
// 
// Flags: (side=-1)
//   (default)                                  a+b+c
//   ghost                                   *  b+c
//   duplicate_corners                          a+b+c+c
//   omit_corners                               a+b
// 
// 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 cells covered does not change.
// 
// Other flags:
//   reverse               Reverses the order in which cells 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.

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

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

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

    IV current[2];
public:
    int side_mask;
    int side;
    IV index;

protected:
    RANGE_ITERATOR()=default;
public:
    RANGE_ITERATOR(const RANGE<IV>& outer,const RANGE<IV>& inner,
        RI flags=RI::none,int side_input=-1);
    RANGE_ITERATOR(const RANGE<IV>& range,int outer_ghost,
        int inner_ghost,RI flags=RI::none,int side_input=-1);
    RANGE_ITERATOR(const IV& counts,int outer_ghost,int inner_ghost,
        RI flags=RI::none,int side_input=-1);
    explicit RANGE_ITERATOR(const RANGE<IV>& range,RI flags=RI::none);
    explicit RANGE_ITERATOR(const IV& counts,int outer_ghost=0,
        RI flags=RI::none);
    ~RANGE_ITERATOR()=default;

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

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

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

    bool Prev_Valid() const PHYSBAM_ALWAYS_INLINE
    {return 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(RI flags,int side_input);

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

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


template<>
class RANGE_ITERATOR<0>
{
    typedef VECTOR<int,0> IV;
public:
    bool first;
    IV index;
    
protected:
    RANGE_ITERATOR()=default;
public:
    RANGE_ITERATOR(const RANGE<IV>& outer,const RANGE<IV>& inner,
        RI flags=RI::none,int side_input=-1){first=true;}
    RANGE_ITERATOR(const RANGE<IV>& range,int outer_ghost,
        int inner_ghost,RI flags=RI::none,int side_input=-1){first=true;}
    RANGE_ITERATOR(const IV& counts,int outer_ghost,int inner_ghost,
        RI flags=RI::none,int side_input=-1){first=true;}
    explicit RANGE_ITERATOR(const RANGE<IV>& range,
        RI flags=RI::none){first=true;}
    explicit RANGE_ITERATOR(const IV& counts,int outer_ghost=0,
        RI flags=RI::none){first=true;}
    ~RANGE_ITERATOR()=default;

    void Next() PHYSBAM_ALWAYS_INLINE
    {first=false;}

    bool Valid() const PHYSBAM_ALWAYS_INLINE
    {return first;}

    void Prev() PHYSBAM_ALWAYS_INLINE
    {first=true;}

    bool Prev_Valid() const PHYSBAM_ALWAYS_INLINE
    {return !first;}

    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(RI flags,int side_input){first=true;}
//#####################################################################
};

template<int d> RANGE_ITERATOR<d> begin(const RANGE<VECTOR<int,d> >& range)
{return RANGE_ITERATOR<d>(range);}

template<int d> RANGE_ITERATOR<d> end(const RANGE<VECTOR<int,d> >& range)
{RANGE_ITERATOR<d> it(range,RI::reverse);it.Next();return it;}

}
#endif
