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

    main.cpp
*/


#include "MultiNode.h"
#include <string>
#include <iostream>
#include <boost/bind.hpp>

using namespace std;

////////////////////////////////////////////////////////////////////////////////
//
//  An example class derived from MultiNode
//
////////////////////////////////////////////////////////////////////////////////

class ExampleObject
    : public MultiNode<ExampleObject>
{
    public:
        static void RunTest ();
   
    public:
        ExampleObject (const string& inName, bool inInteresting = false)
            : mName(inName)
            , mInteresting(inInteresting)
        {
        }

        const string& ToString () const {
            return mName;
        }

    // Some example predicates

        bool IsInteresting () const {
            return mInteresting;
        }
       
        bool NameContains (const string& inSubstring) const {
            return (mName.find( inSubstring ) != string::npos);
        }

    private:
        string mName;
        bool mInteresting;
};

////////////////////////////////////////////////////////////////////////////////
//
//  Family IDs
//
////////////////////////////////////////////////////////////////////////////////

enum Family
{
    MainFamily,
    SecondFamily
};

////////////////////////////////////////////////////////////////////////////////
//
//  Example
//
////////////////////////////////////////////////////////////////////////////////

void
ExampleObject::RunTest ()
{
    Ref parent (new ExampleObject ("parent", true)); // Mark as interesting
    Ref child1 (new ExampleObject ("child1"));
    Ref child2 (new ExampleObject ("child2", true)); // Mark as interesting
    Ref grandchild1 (new ExampleObject ("grandchild1"));
    Ref grandchild2 (new ExampleObject ("grandchild2"));
    Ref grandchild3 (new ExampleObject ("grandchild3", true)); // Mark as interesting

// Build tree structure:
//
//  -- (MainFamily) --
//
//  parent
//      child1
//          grandchild1
//      child2
//          grandchild2
//          grandchild3
//

    parent->AddChildIn( MainFamily, child1 );
    parent->AddChildIn( MainFamily, child2 );
    child1->AddChildIn( MainFamily, grandchild1 );
    child2->AddChildIn( MainFamily, grandchild2 );
    child2->AddChildIn( MainFamily, grandchild3 );

    cout << "-- (MainFamily) --\n"
        << "parent\n"
        << "  child1\n"
        << "    grandchild1\n"
        << "  child2\n"
        << "    grandchild2\n"
        << "    grandchild3\n"
        << "\n";

//  -- (SecondFamily) --
//
//  child2
//      child1
//          grandchild2
//              grandchild1

    child2->AddChildIn( SecondFamily, child1 );
    child1->AddChildIn( SecondFamily, grandchild2 );
    grandchild2->AddChildIn( SecondFamily, grandchild1 );

    cout << "-- (SecondFamily) --\n"
        << "child2\n"
        << "  child1\n"
        << "    grandchild2\n"
        << "    grandchild1\n"
        << "\n";


// Visit some interesting objects

    cout << "Visit 1 (all interesting objects):" << endl;
    for (tree_iterator i (parent, &ExampleObject::IsInteresting); i; ++i)
    {
        // Do something with the object...

        cout << "- " << i->ToString() << " visited." << endl;
    }
    cout << endl;

// Visit a different set of objects

    cout << "Visit 2 (children of child2 with 3 in their name):" << endl;
    for (child_iterator i (child2, boost::bind( &ExampleObject::NameContains, _1, "3" )); i; ++i)
    {
        cout << "- " << i->ToString() << " visited." << endl;
    }
    cout << endl;

// etc.

    cout << "Visit 3 (parents of grandchild1, including self):" << endl;
    for (parent_iterator i (grandchild1); i; ++i)
    {
        cout << "- " << i->ToString() << " visited." << endl;
    }
    cout << endl;

    cout << "Visit 4 (the other family):" << endl;
    for (tree_iterator i (child2, SecondFamily); i; ++i)
    {
        cout << "- " << i->ToString() << " visited." << endl;
    }
    cout << endl;
}


////////////////////////////////////////////////////////////////////////////////
//
//  main
//
////////////////////////////////////////////////////////////////////////////////

int main ()
{
    ExampleObject::RunTest();
    return 0;
}