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

    Use, modification and distribution is subject to the Boost Software
    License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
    http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_ast.hpp>
#include <boost/spirit/include/classic_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 <boost/core/lightweight_test.hpp>
#include "impl/string_length.hpp"

#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_CLASSIC_NS;
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 + test_impl::string_length(text_begin);

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

        BOOST_TEST(info.full);

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

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

        BOOST_TEST(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;
        BOOST_TEST(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());

    return boost::report_errors();
}
