/*
    MultiNode sample code
    Henry Smith (henry@enigmasoftware.ca)

    MultiNode template class from which concrete classes should derive
*/


#ifndef MultiNode_H
#define MultiNode_H

#include "MultiNodeBase.h"
#include <boost/function.hpp>

////////////////////////////////////////////////////////////////////////////////
//
//  Iteration orders
//  (to be used as template parameters to MultiNode::iterator)
//
////////////////////////////////////////////////////////////////////////////////

// Sample tree:
//
//   A
//   |
//   +--B
//   |
//   +--C
//   |  |
//   |  +--D
//   |
//   +--E
//

// TreeOrder (with root A iterates B, C, D, E)

struct TreeOrder
{
    static MultiNodeRef Start (const MultiNodeRef& inRoot, bool inShouldIncludeRoot = false);
    static MultiNodeRef Advance (const MultiNodeRef& inPosition);

    private:
        static MultiNodeRef NextNode (const MultiNodeRef& inCurrent);
};

// ChildOrder (with root A iterates B, C, E)

struct ChildOrder
{
    static MultiNodeRef Start (const MultiNodeRef& inRoot, bool inShouldIncludeRoot = false);
    static MultiNodeRef Advance (const MultiNodeRef& inPosition);
};

// TreeOrder (with root D iterates C, A)

struct ParentOrder
{
    static MultiNodeRef Start (const MultiNodeRef& inRoot, bool inShouldIncludeRoot = false);
    static MultiNodeRef Advance (const MultiNodeRef& inPosition);
};

enum
{
    DontIncludeRoot = false,
    IncludeRoot     = true
};


////////////////////////////////////////////////////////////////////////////////
//
//  MultiNode
//  (templatize with subclass, ie. class MyData : public MultiNode<MyData>...)
//
////////////////////////////////////////////////////////////////////////////////


template <class T>
class MultiNode
    : public MultiNodeBase
{
    typedef MultiNodeBase Super;

    public:
        typedef MultiNodeRefCast<T> Ref;

    public:
        MultiNode () {}
        virtual ~MultiNode () {}

        ////////////////////////////////////////////////////////////////////////////////
        //
        //  Predicates
        //
        ////////////////////////////////////////////////////////////////////////////////
   
        typedef boost::function<bool (const T*)> Predicate;

        // The default predicate (always returns true)

        struct NullPredicate {
            bool operator () (const T*) const { return true; }
        };

        ////////////////////////////////////////////////////////////////////////////////
        //
        //  Iterator template
        //
        ////////////////////////////////////////////////////////////////////////////////

        template <class Order, bool ShouldIncludeRoot, FamilyID Family>
        struct iterator
        {
            public:
                iterator (const Ref& inRoot)
                    : mFamilyScope(Family)
                    , mPredicate(sNullPredicate)
                    , mNode(Start( inRoot, ShouldIncludeRoot )) {}

            // Alternate constructors for selectively overriding the template parameters

                iterator (const Ref& inRoot, FamilyID inFamily, bool inShouldIncludeRoot = ShouldIncludeRoot)
                    : mFamilyScope(inFamily)
                    , mPredicate(sNullPredicate)
                    , mNode(Start( inRoot, inShouldIncludeRoot )) {}

                iterator (const Ref& inRoot, FamilyID inFamily, Predicate inPredicate)
                    : mFamilyScope(inFamily)
                    , mPredicate(inPredicate)
                    , mNode(Start( inRoot, ShouldIncludeRoot )) {}

                iterator (const Ref& inRoot, Predicate inPredicate)
                    : mFamilyScope(Family)
                    , mPredicate(inPredicate)
                    , mNode(Start( inRoot, ShouldIncludeRoot )) {}


                            operator T*     () const { return mNode.GetPtr(); }
                T*          operator ->     () const { return mNode.GetPtr(); }
                T*          operator *      () const { return mNode.GetPtr(); }
                iterator&   operator ++     () { mNode = Advance( mNode ); return (*this); }

            protected:
                Ref         Start           (const Ref& inRoot, bool inShouldIncludeRoot);
                Ref         Advance         (const Ref& inPosition);

            private:
                FamilyScope mFamilyScope;   // This declaration has to be first! (we need a family to start in...)
                Predicate mPredicate;
                Ref mNode;
               
            private:
                static NullPredicate sNullPredicate;
        };

   
        ////////////////////////////////////////////////////////////////////////////////
        //
        //  Helpful specializations for basic iteration
        //
        ////////////////////////////////////////////////////////////////////////////////

        typedef iterator< TreeOrder, IncludeRoot, 0 > tree_iterator;
        friend struct iterator< TreeOrder, IncludeRoot, 0 >;

        typedef iterator< ChildOrder, DontIncludeRoot, 0 > child_iterator;
        friend struct iterator< ChildOrder, DontIncludeRoot, 0 >;

        typedef iterator< ParentOrder, IncludeRoot, 0 > parent_iterator;
        friend struct iterator< ParentOrder, IncludeRoot, 0 >;
};


////////////////////////////////////////////////////////////////////////////////
//
//  Template function definitions
//
////////////////////////////////////////////////////////////////////////////////

template <class T>
template <class O, bool S, FamilyID F>
typename MultiNode<T>::NullPredicate MultiNode<T>::iterator<O, S, F>::sNullPredicate;

template <class T>
template <class O, bool S, FamilyID F>
MultiNodeRefCast<T> MultiNode<T>::iterator<O, S, F>::Start (const Ref& inRoot, bool inShouldIncludeRoot)
{
    Ref start = O::Start( inRoot, inShouldIncludeRoot );

    // Skip over all nodes that DON'T match the condition (ie. only visit nodes that satisfy the condition)
    while (start != NullRef && !mPredicate( start.GetPtr() )) {
        start = O::Advance( start );
    }
    return start;
}

template <class T>
template <class O, bool S, FamilyID F>
MultiNodeRefCast<T> MultiNode<T>::iterator<O, S, F>::Advance (const Ref& inPosition)
{
    Ref next = O::Advance( inPosition );

    // Skip over all nodes that DON'T match the condition (ie. only visit nodes that satisfy the condition)
    while (next != NullRef && !mPredicate( next.GetPtr() )) {
        next = O::Advance( next );
    }
    return next;
}

#endif  // MultiNode_H