/*=============================================================================
    Copyright (c) 2010-2011 Daniel James
    Copyright (c) 2003 Martin Wille
    http://spirit.sourceforge.net/

  Distributed under 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)
 =============================================================================*/

// Some custom parsers for use in quickbook.
 
#ifndef BOOST_QUICKBOOK_PARSERS_HPP
#define BOOST_QUICKBOOK_PARSERS_HPP

#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_nil.hpp>
#include <boost/spirit/include/phoenix1_primitives.hpp>
#include <boost/spirit/include/phoenix1_tuples.hpp>
#include <boost/spirit/include/phoenix1_binders.hpp>

namespace quickbook {
    namespace cl = boost::spirit::classic;
    
    ///////////////////////////////////////////////////////////////////////////
    //
    // scoped_parser<Impl>
    //
    // Impl is a struct with the methods:
    //
    // void start();
    // void success();
    // void failure();
    // void cleanup();
    //
    ///////////////////////////////////////////////////////////////////////////

    template <typename Impl, typename Arguments, typename ParserT>
    struct scoped_parser_impl
        : public cl::unary< ParserT, cl::parser< scoped_parser_impl<Impl, Arguments, ParserT> > >
    {
        typedef scoped_parser_impl<Impl, Arguments, ParserT> self_t;
        typedef cl::unary< ParserT, cl::parser< scoped_parser_impl<Impl, Arguments, ParserT> > > base_t;

        template <typename ScannerT>
        struct result { typedef cl::match<> type; };

        scoped_parser_impl(
                Impl const& impl,
                Arguments const& arguments,
                ParserT const &p)
            : base_t(p)
            , impl_(impl)
            , arguments_(arguments)
        {}

        struct scoped
        {
            explicit scoped(Impl const& impl)
                : impl_(impl)
                , in_progress_(false)
            {}
            
            typedef phoenix::tuple_index<0> t0;
            typedef phoenix::tuple_index<1> t1;
            
            bool start(phoenix::tuple<> const&)
            {
                in_progress_ = impl_.start();
                return in_progress_;
            }
            
            template <typename Arg1>
            bool start(phoenix::tuple<Arg1> const& x)
            {
                in_progress_ = phoenix::bind(&Impl::start)(phoenix::var(impl_), x[t0()])();
                return in_progress_;
            }

            template <typename Arg1, typename Arg2>
            bool start(phoenix::tuple<Arg1, Arg2> const& x)
            {
                in_progress_ = phoenix::bind(&Impl::start)(phoenix::var(impl_), x[t0()], x[t1()])();
                return in_progress_;
            }
            
            void success()
            {
                in_progress_ = false;
                impl_.success();
            }

            void failure()
            {
                in_progress_ = false;
                impl_.failure();
            }

            ~scoped()
            {
                if (in_progress_) impl_.failure();
                impl_.cleanup();
            }
            
            Impl impl_;
            bool in_progress_;
        };
    
        template <typename ScannerT>
        typename result<ScannerT>::type parse(ScannerT const &scan) const
        {
            typedef typename ScannerT::iterator_t iterator_t;
            iterator_t save = scan.first;

            scoped scope(impl_);
            if (!scope.start(arguments_))
                return scan.no_match();

            typename cl::parser_result<ParserT, ScannerT>::type result
                = this->subject().parse(scan);

            bool success = scope.impl_.result(result, scan);

            if (success) {
                scope.success();
                if (result) {
                    return scan.create_match(result.length(), cl::nil_t(), save, scan.first);
                }
                else {
                    return scan.create_match(scan.first.base() - save.base(), cl::nil_t(), save, scan.first);
                }
            }
            else {
                scope.failure();
                return scan.no_match();
            }
        }
        
        Impl impl_;
        Arguments arguments_;
    };

    template <typename Impl, typename Arguments>
    struct scoped_parser_gen
    {    
        explicit scoped_parser_gen(Impl impl, Arguments const& arguments)
            : impl_(impl), arguments_(arguments) {}

        template<typename ParserT>
        scoped_parser_impl
        <
            Impl,
            Arguments,
            typename cl::as_parser<ParserT>::type
        >
        operator[](ParserT const &p) const
        {
            typedef cl::as_parser<ParserT> as_parser_t;
            typedef typename as_parser_t::type parser_t;

            return scoped_parser_impl<Impl, Arguments, parser_t>
                (impl_, arguments_, p);
        }

        Impl impl_;
        Arguments arguments_;
    };

    template <typename Impl>
    struct scoped_parser
    {
        scoped_parser(Impl const& impl)
            : impl_(impl) {}
    
        scoped_parser_gen<Impl, phoenix::tuple<> >
            operator()() const
        {
            typedef phoenix::tuple<> tuple;
            return scoped_parser_gen<Impl, tuple>(impl_, tuple());
        }

        template <typename Arg1>
        scoped_parser_gen<Impl, phoenix::tuple<Arg1> >
            operator()(Arg1 x1) const
        {
            typedef phoenix::tuple<Arg1> tuple;
            return scoped_parser_gen<Impl, tuple>(impl_, tuple(x1));
        }
    
        template <typename Arg1, typename Arg2>
        scoped_parser_gen<Impl, phoenix::tuple<Arg1, Arg2> >
            operator()(Arg1 x1, Arg2 x2) const
        {
            typedef phoenix::tuple<Arg1, Arg2> tuple;
            return scoped_parser_gen<Impl, tuple>(impl_, tuple(x1, x2));
        }
        
        Impl impl_;
    };
    
    ///////////////////////////////////////////////////////////////////////////
    //
    // Lookback parser
    //
    // usage: lookback[body]
    //
    // Requires that iterator has typedef 'lookback_range' and function
    // 'lookback' returning a 'lookback_range'.
    //
    ///////////////////////////////////////////////////////////////////////////

    template <typename ParserT>
    struct lookback_parser
        : public cl::unary< ParserT, cl::parser< lookback_parser<ParserT> > >
    {
        typedef lookback_parser<ParserT> self_t;
        typedef cl::unary< ParserT, cl::parser< lookback_parser<ParserT> > > base_t;

        template <typename ScannerT>
        struct result
        {
            typedef typename cl::parser_result<ParserT, ScannerT>::type type;
        };

        lookback_parser(ParserT const& p)
            : base_t(p)
        {}

        template <typename ScannerT>
        typename result<ScannerT>::type parse(ScannerT const &scan) const
        {
            typedef typename ScannerT::iterator_t::lookback_range::iterator iterator_t;
            typedef cl::scanner<iterator_t, typename ScannerT::policies_t> scanner_t;

            iterator_t begin = scan.first.lookback().begin();
            scanner_t lookback_scan(begin, scan.first.lookback().end(), scan);
            
            if (this->subject().parse(lookback_scan))
                return scan.empty_match();
            else
                return scan.no_match();
        }
    };
    
    struct lookback_gen
    {
        template <typename ParserT>
        lookback_parser<ParserT> operator[](ParserT const& p) const
        {
            return lookback_parser<ParserT>(p);
        }
    };
    
    lookback_gen const lookback = lookback_gen();
}

#endif // BOOST_QUICKBOOK_SCOPED_BLOCK_HPP
