/*=============================================================================
    Spirit V1.6.1
    Copyright (c) 2003 Giovanni Bajo
    http://spirit.sourceforge.net/

    Permission to copy, use, modify, sell and distribute this software
    is granted provided this copyright notice appears in all copies.
    This software is provided "as is" without express or implied
    warranty, and with no claim as to its suitability for any purpose.
=============================================================================*/
#include <boost/spirit/core.hpp>
#include <boost/spirit/tree/ast.hpp>
#include <boost/spirit/tree/tree_to_xml.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/repetition.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/mpl/list.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/remove.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/for_each.hpp>

#include <map>
#include <string>

#include <fstream>
#include <assert.h>

#define DEBUG_DUMP_TREES    (1)

//////////////////////////////////////////////////////////////////////////////
// rule_id helper
// http://sf.net/tracker/index.php?func=detail&aid=715483&group_id=28447&atid=393389)

namespace boost { namespace spirit {

    template <
        typename ScannerT, 
        unsigned long ID = 0,
        typename ContextT = parser_context>
    class rule_id 
        : public rule<ScannerT, ContextT, parser_tag<ID> >
    {
        typedef rule<ScannerT, ContextT, parser_tag<ID> > base_t;

    public:
        // Forward ctors and operator=.
        rule_id()
        {}

        template <typename T>
        rule_id(const T& a) 
        : base_t(a) {}

        template <typename T>
        rule_id& operator=(const T& a)
        { base_t::operator=(a); return *this; }
    };
}}


//////////////////////////////////////////////////////////////////////////////
// Framework setup

namespace mpl = boost::mpl;
using namespace boost::spirit;
using namespace std;


enum RULE_ID
{
    ID_A = 1,
    ID_B,
    ID_C,
    ID_ROOT
};

map<parser_id, string> rule_names;


//////////////////////////////////////////////////////////////////////////////
// Generic tree manipulation tools

template <typename TreeT>
RULE_ID id(TreeT& t)
{ return (RULE_ID)t.value.id().to_long(); }


template <typename TreeT>
TreeT& child(TreeT& t, unsigned n)
{
    return t.children[n];
}

template <typename TreeT>
size_t num_children(const TreeT& t)
{ return t.children.size(); }

template <typename TreeT>
typename TreeT::parse_node_t::iterator_t ValueBeginIterator(TreeT& t)
{ return t.value.begin(); }

template <typename TreeT>
typename TreeT::parse_node_t::iterator_t ValueEndIterator(TreeT& t)
{ return t.value.end(); }

template <typename TreeT>
bool equal(TreeT& a, TreeT& b)
{
    if (id(a) != id(b))
        return false;

    if (num_children(a) != num_children(b))
        return false;

    unsigned n = num_children(a);
    for (unsigned i=0;i<n;i++)
        if (!equal(child(a, i), child(b, i)))
            return false;

    return true;
}

template <typename TreeT>
void dump(ostream& o, TreeT& t, int level = 0)
{
    string name;
    string value;
    map<parser_id, string>::iterator iter = 
        rule_names.find(id(t));

    if (iter == rule_names.end())
        name = "noname";
    else
        name = iter->second;

    value.assign(ValueBeginIterator(t), ValueEndIterator(t));

    for (int i=0;i<level;i++)
        o << "  ";


    o << name << ": " << value << endl;
    
    unsigned n = num_children(t);
    for (unsigned c=0;c<n;c++)
        dump(o, child(t, c), level+1);
}


//////////////////////////////////////////////////////////////////////////////
// Tree folding

namespace test_impl {

    template <typename ParmT>
    struct fold_node
    {
        // assign a subtree
        void operator()(ParmT& t, ParmT ch) const
        { t = ch; }

        // wrong specialization
        template <typename TreeT>
        void operator()(TreeT& t, ParmT p) const
        { typedef typename TreeT::this_should_never_be_compiled type; }
    };

    template <>
    struct fold_node<nil_t>
    {
        template <typename TreeT>
        void operator()(TreeT& t, nil_t) const
        { typedef typename TreeT::this_should_never_be_compiled type; }
    };

    template <>
    struct fold_node<RULE_ID>
    {
        template <typename TreeT>
        void operator()(TreeT& t, RULE_ID id) const
        { t.value.id(id); }
    };

    template <typename ParmT>
    struct fold_child
    {
        template <typename TreeT>
        void operator()(TreeT& t, ParmT p, unsigned n) const
        { fold_node<ParmT>()(t.children[n], p); }
    };

    template <>
    struct fold_child<nil_t>
    {
        template <typename TreeT>
        void operator()(TreeT& t, nil_t, unsigned n) const
        {}
    };
}

template <typename TreeT,
        typename T, typename T1, typename T2, typename T3, typename T4,
        typename T5, typename T6, typename T7, typename T8>
TreeT fold(T p, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8)
{
    // Prepare a list with all the template types
    typedef mpl::list<T1,T2,T3,T4,T5,T6,T7,T8> full_list_t;

    // Remove the ones equal to nil_t: they are the default parameters
    //  unspecified from the user
    typedef typename mpl::remove<full_list_t, nil_t>::type parm_list_t;

    // Get the size of the list = number of parameters specified by the user
    typedef typename mpl::size<parm_list_t>::type parm_list_size_t;
    enum { NUM_CHILDREN = parm_list_size_t::value };

    TreeT t;

    // Generate the root of the tree (specialized for the first parameter)
    test_impl::fold_node<T>()(t, p);

    // Make room for the children
    if (NUM_CHILDREN > 0)
        t.children.resize(NUM_CHILDREN);

    // For each children, call the GenerateChild function, which is specialized
    //  on the different types
    test_impl::fold_child<T1>()(t, p1, 0);
    test_impl::fold_child<T2>()(t, p2, 1);
    test_impl::fold_child<T3>()(t, p3, 2);
    test_impl::fold_child<T4>()(t, p4, 3);
    test_impl::fold_child<T5>()(t, p5, 4);
    test_impl::fold_child<T6>()(t, p6, 5);
    test_impl::fold_child<T7>()(t, p7, 6);
    test_impl::fold_child<T8>()(t, p8, 7);

    return t;
}


// Define fold() wrapper for 1->7 parameters: they just call the 8 parameter
//  version passing nil_t for the other arguments
#define PUT_EMPTY(Z, N, _)  nil_t()

#define DEFINE_FOLD(Z, N, _) \
    template <typename TreeT, typename T BOOST_PP_COMMA_IF(N) \
        BOOST_PP_ENUM_PARAMS(N, typename T) > \
    TreeT fold(T p BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_BINARY_PARAMS(N, T, p)) \
    { \
        return fold<TreeT>(p \
            BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_PARAMS(N, p) \
            BOOST_PP_COMMA_IF(BOOST_PP_SUB(8,N))  \
            BOOST_PP_ENUM(BOOST_PP_SUB(8,N), PUT_EMPTY, _)); \
    }

BOOST_PP_REPEAT(7, DEFINE_FOLD, _)

#undef PUT_EMPTY
#undef DEFINE_FOLD



//////////////////////////////////////////////////////////////////////////////
// test_banal: simple tree construction

struct test_banal : public grammar<test_banal>
{
    template <class T>
    struct definition
    {
        rule_id<T, ID_ROOT> root;
        rule_id<T, ID_A> a;
        rule_id<T, ID_B> b;
        rule_id<T, ID_C> c;

        definition(const test_banal&)
        {
            root = a >> c;
            a = b;
            b = chlit<>('b');
            c = chlit<>('c');
        }

        const rule_id<T, ID_ROOT>& start() 
        { return root; }
    };

    const char* pattern(void)
    {
        return "bc";
    }

    template <typename TreeT>
    TreeT expected_tree(void)
    {
        return fold<TreeT>(
            ID_ROOT, fold<TreeT>(
                ID_A, 
                    ID_B), 
                ID_C);
    }
};


//////////////////////////////////////////////////////////////////////////////
// All the tests

typedef mpl::list
<
    test_banal

> tests_t;


//////////////////////////////////////////////////////////////////////////////
// run_test - code to run a test

struct run_test
{
    template <typename TestT>
    void operator()(TestT gram)
    {
        typedef const char* iterator_t;
        typedef node_val_data_factory<nil_t> factory_t;
        typedef typename 
            factory_t
            ::BOOST_NESTED_TEMPLATE factory<iterator_t>
            ::node_t node_t;
        typedef tree_node<node_t> tree_t;

        iterator_t text_begin = gram.pattern();
        iterator_t text_end = text_begin + strlen(text_begin);

        tree_parse_info<iterator_t, factory_t> info =
            ast_parse(text_begin, text_end, gram);

        assert(info.full);

        tree_t expected = gram.template expected_tree<tree_t>();

#if DEBUG_DUMP_TREES
        dump(cout, info.trees[0]);
        dump(cout, expected);
#endif

        assert(equal(info.trees[0], expected));
    }
};

//////////////////////////////////////////////////////////////////////////////
// main() stuff

#ifdef BOOST_NO_EXCEPTIONS
namespace boost
{
    void throw_exception(std::exception const & )
    {
        std::cerr << "Exception caught" << std::endl;
        assert(0);
    }
}

#endif


void init(void)
{
    rule_names[ID_ROOT] = "ID_ROOT";
    rule_names[ID_A] = "ID_A";
    rule_names[ID_B] = "ID_B";
    rule_names[ID_C] = "ID_C";
}


int main()
{
    init();

    mpl::for_each<tests_t, mpl::_> (run_test());

	cout << "Test completed successfully" << endl;

    return 0;
}
