#ifndef PASTELSYS_VIEW_VISIT_HPP
#define PASTELSYS_VIEW_VISIT_HPP
#include "pastel/sys/view/view_visit.h"
namespace Pastel
{
    // Traversal without a view
    namespace VisitRectangle_
    {
        class NormalTag {};
        class TerminateTag {};
        template <integer Index, integer N, typename VisitRectangleFunctor>
        void visitRectangleDimension(
            const Vector<integer, N>& extent,
            const Vector<integer, N>& startPosition,
            const VisitRectangleFunctor& visitRectangle,
            NormalTag)
        {
            ASSERT2(Index > 0 && Index < N, Index, N);
            integer width = extent[Index];
            Vector<integer, N> position = startPosition;
            for (integer i = 0;i < width;++i)
            {
                typedef typename boost::mpl::if_c<
                    (Index > 1),
                    NormalTag,
                    TerminateTag>::type Tag;
                visitRectangleDimension<Index - 1>(
                    extent, position, visitRectangle, Tag());
                ++position[Index];
            }
        }
        template <integer Index, integer N, typename VisitRectangleFunctor>
        void visitRectangleDimension(
            const Vector<integer, N>& extent,
            const Vector<integer, N>& startPosition,
            const VisitRectangleFunctor& visitRectangle,
            TerminateTag)
        {
            ASSERT2(Index >= 0 && Index < N, Index, N);
            integer width = extent[Index];
            Vector<integer, N> position = startPosition;
            for (integer i = 0;i < width;++i)
            {
                visitRectangle(position);
                ++position[Index];
            }
        }
    }
    template <integer N, typename VisitorFunctor>
    void visit(
        const AlignedBox<integer, N>& rectangle,
        const VisitorFunctor& visitor)
    {
        typedef typename boost::mpl::if_c<
            (N > 1),
            VisitRectangle_::NormalTag,
            VisitRectangle_::TerminateTag>::type Tag;
        VisitRectangle_::visitRectangleDimension<N - 1>(
            evaluate(rectangle.extent()),
            Vector<integer, N>(0),
            visitor,
            Tag());
    }
    // Single view traversal
    namespace Visit_
    {
        class NormalTag {};
        class TerminateTag {};
        template <integer Index, integer N, typename Cursor, typename VisitFunctor>
        void visitDimension(
            const Vector<integer, N>& extent,
            const Cursor& startCursor,
            const VisitFunctor& visitor,
            TerminateTag)
        {
            ASSERT2(Index >= 0 && Index < N, Index, N);
            integer width = extent[Index];
            Cursor cursor = startCursor;
            for (integer i = 0;i < width;++i)
            {
                visitor(*cursor);
                cursor.increment(Index);
            }
        }
        template <integer Index, integer N, typename Cursor, typename VisitFunctor>
        void visitDimension(
            const Vector<integer, N>& extent,
            const Cursor& startCursor,
            const VisitFunctor& visitor,
            NormalTag)
        {
            ASSERT2(Index > 0 && Index < N, Index, N);
            integer width = extent[Index];
            Cursor cursor = startCursor;
            for (integer i = 0;i < width;++i)
            {
                typedef typename boost::mpl::if_c<
                    (Index > 1),
                    NormalTag,
                    TerminateTag>::type Tag;
                visitDimension<Index - 1>(
                    extent, cursor, visitor, Tag());
                cursor.increment(Index);
            }
        }
    }
    template <integer N, typename Input_Element, typename Input_ConstView, typename VisitFunctor>
    void visit(
        const ConstView<N, Input_Element, Input_ConstView>& input,
        const VisitFunctor& visitor)
    {
        typedef typename boost::mpl::if_c<
            (N > 1),
            Visit_::NormalTag,
            Visit_::TerminateTag>::type Tag;
        Visit_::visitDimension<N - 1>(
            input.extent(),
            input.constCursor(Vector<integer, N>(0)),
            visitor,
            Tag());
    }
    template <integer N, typename Input_Element, typename Input_View, typename VisitFunctor>
    void visit(
        const View<N, Input_Element, Input_View>& input,
        const VisitFunctor& visitor)
    {
        typedef typename boost::mpl::if_c<
            (N > 1),
            Visit_::NormalTag,
            Visit_::TerminateTag>::type Tag;
        Visit_::visitDimension<N - 1>(
            input.extent(),
            input.cursor(Vector<integer, N>(0)),
            visitor,
            Tag());
    }
    // Two-view parallel traversal
    namespace Visit_
    {
        template <integer Index, integer N,
            typename Left_Cursor,
            typename Right_Cursor,
            typename VisitFunctor>
        void visitDimension(
            const Vector<integer, N>& extent,
            const Left_Cursor& leftStartCursor,
            const Right_Cursor&  rightStartCursor,
            const VisitFunctor& visitor,
            NormalTag)
        {
            ASSERT2(Index > 0 && Index < N, Index, N);
            integer width = extent[Index];
            Left_Cursor leftCursor = leftStartCursor;
            Right_Cursor rightCursor = rightStartCursor;
            for (integer i = 0;i < width;++i)
            {
                typedef typename boost::mpl::if_c<
                    (Index > 1),
                    NormalTag,
                    TerminateTag>::type Tag;
                visitDimension<Index - 1>(
                    extent, leftCursor, rightCursor, visitor, Tag());
                leftCursor.increment(Index);
                rightCursor.increment(Index);
            }
        }
        template <integer Index, integer N,
            typename Left_Cursor,
            typename Right_Cursor,
            typename VisitFunctor>
        void visitDimension(
            const Vector<integer, N>& extent,
            const Left_Cursor& leftStartCursor,
            const Right_Cursor& rightStartCursor,
            const VisitFunctor& visitor,
            TerminateTag)
        {
            ASSERT2(Index >= 0 && Index < N, Index, N);
            integer width = extent[Index];
            Left_Cursor leftCursor = leftStartCursor;
            Right_Cursor rightCursor = rightStartCursor;
            for (integer i = 0;i < width;++i)
            {
                visitor(*leftCursor, *rightCursor);
                leftCursor.increment(Index);
                rightCursor.increment(Index);
            }
        }
    }
    template <integer N,
        typename Left_Element, typename Left_ConstView,
        typename Right_Element, typename Right_View,
        typename VisitFunctor>
    void visit(
        const ConstView<N, Left_Element, Left_ConstView>& left,
        const View<N, Right_Element, Right_View>& right,
        const VisitFunctor& visitor)
    {
        ENSURE(left.extent() == right.extent());
        typedef typename boost::mpl::if_c<
            (N > 1),
            Visit_::NormalTag,
            Visit_::TerminateTag>::type Tag;
        Visit_::visitDimension<N - 1>(
            left.extent(),
            left.constCursor(Vector<integer, N>(0)),
            right.cursor(Vector<integer, N>(0)),
            visitor,
            Tag());
    }
    template <integer N,
        typename Left_Element, typename Left_View,
        typename Right_Element, typename Right_View,
        typename VisitFunctor>
    void visit(
        const View<N, Left_Element, Left_View>& left,
        const View<N, Right_Element, Right_View>& right,
        const VisitFunctor& visitor)
    {
        ENSURE(left.extent() == right.extent());
        typedef typename boost::mpl::if_c<
            (N > 1),
            Visit_::NormalTag,
            Visit_::TerminateTag>::type Tag;
        Visit_::visitDimension<N - 1>(
            left.extent(),
            left.cursor(Vector<integer, N>(0)),
            right.cursor(Vector<integer, N>(0)),
            visitor,
            Tag());
    }
    template <integer N,
        typename Left_Element, typename Left_ConstView,
        typename Right_Element, typename Right_ConstView,
        typename VisitFunctor>
    void visit(
        const ConstView<N, Left_Element, Left_ConstView>& left,
        const ConstView<N, Right_Element, Right_ConstView>& right,
        const VisitFunctor& visitor)
    {
        ENSURE(left.extent() == right.extent());
        typedef typename boost::mpl::if_c<
            (N > 1),
            Visit_::NormalTag,
            Visit_::TerminateTag>::type Tag;
        Visit_::visitDimension<N - 1>(
            left.extent(),
            left.constCursor(Vector<integer, N>(0)),
            right.constCursor(Vector<integer, N>(0)),
            visitor,
            Tag());
    }
}
#endif