/*=============================================================================
    Copyright (c) 2001-2011 Joel de Guzman

    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)
==============================================================================*/
#if !defined(BOOST_SPIRIT_ERROR_HANDLER_APRIL_29_2007_1042PM)
#define BOOST_SPIRIT_ERROR_HANDLER_APRIL_29_2007_1042PM

#if defined(_MSC_VER)
#pragma once
#endif

#include <boost/spirit/home/qi/operator/expect.hpp>
#include <boost/spirit/home/qi/nonterminal/rule.hpp>
#include <boost/spirit/home/support/multi_pass_wrapper.hpp>
#include <boost/function.hpp>
#include <boost/assert.hpp>

namespace boost { namespace spirit { namespace qi
{
    enum error_handler_result
    {
        fail
      , retry
      , accept
      , rethrow
    };

    namespace detail
    {
        // Helper template allowing to manage the inhibit clear queue flag in
        // a multi_pass iterator. This is the usual specialization used for
        // anything but a multi_pass iterator.
        template <typename Iterator, bool active>
        struct reset_on_exit
        {
            reset_on_exit(Iterator&) {}
        };

        // For 'retry' or 'fail' error handlers we need to inhibit the flushing 
        // of the internal multi_pass buffers which otherwise might happen at 
        // deterministic expectation points inside the encapsulated right hand 
        // side of rule.
        template <typename Iterator>
        struct reset_on_exit<Iterator, true>
        {
            reset_on_exit(Iterator& it)
              : it_(it)
              , inhibit_clear_queue_(spirit::traits::inhibit_clear_queue(it)) 
            {
                spirit::traits::inhibit_clear_queue(it_, true);
            }

            ~reset_on_exit()
            {
                // reset inhibit flag in multi_pass on exit
                spirit::traits::inhibit_clear_queue(it_, inhibit_clear_queue_);
            }

            Iterator& it_;
            bool inhibit_clear_queue_;
        };
    }

    template <
        typename Iterator, typename Context
      , typename Skipper, typename F, error_handler_result action
    >
    struct error_handler
    {
        typedef function<
            bool(Iterator& first, Iterator const& last
              , Context& context
              , Skipper const& skipper
            )>
        function_type;

        error_handler(function_type subject, F f)
          : subject(subject)
          , f(f)
        {
        }

        bool operator()(
            Iterator& first, Iterator const& last
          , Context& context, Skipper const& skipper) const
        {
            typedef qi::detail::reset_on_exit<Iterator
              , traits::is_multi_pass<Iterator>::value && 
                  (action == retry || action == fail)> on_exit_type;

            on_exit_type on_exit(first);
            for(;;)
            {
                try
                {
                    Iterator i = first;
                    bool r = subject(i, last, context, skipper);
                    if (r)
                        first = i;
                    return r;
                }
                catch (expectation_failure<Iterator> const& x)
                {
                    typedef
                        fusion::vector<
                            Iterator&
                          , Iterator const&
                          , Iterator const&
                          , info const&>
                    params;
                    error_handler_result r = action;
                    params args(first, last, x.first, x.what_);
                    f(args, context, r);

                    // The assertions below will fire if you are using a
                    // multi_pass as the underlying iterator, one of your error
                    // handlers forced its guarded rule to 'fail' or 'retry',
                    // and the error handler has not been instantiated using
                    // either 'fail' or 'retry' in the first place. Please see 
                    // the multi_pass docs for more information.
                    switch (r)
                    {
                        case fail: 
                            BOOST_ASSERT(
                                !traits::is_multi_pass<Iterator>::value ||
                                    action == retry || action == fail);
                            return false;
                        case retry: 
                            BOOST_ASSERT(
                                !traits::is_multi_pass<Iterator>::value ||
                                    action == retry || action == fail);
                            continue;
                        case accept: return true;
                        case rethrow: boost::throw_exception(x);
                    }
                }
            }
            return false;
        }

        function_type subject;
        F f;
    };

    template <
        error_handler_result action
      , typename Iterator, typename T0, typename T1, typename T2
      , typename F>
    void on_error(rule<Iterator, T0, T1, T2>& r, F f)
    {
        typedef rule<Iterator, T0, T1, T2> rule_type;

        typedef
            error_handler<
                Iterator
              , typename rule_type::context_type
              , typename rule_type::skipper_type
              , F
              , action>
        error_handler;
        r.f = error_handler(r.f, f);
    }

    // Error handling support when <action> is not
    // specified. We will default to <fail>.
    template <typename Iterator, typename T0, typename T1
      , typename T2, typename F>
    void on_error(rule<Iterator, T0, T1, T2>& r, F f)
    {
        on_error<fail>(r, f);
    }
}}}

#endif
