/*=============================================================================
    Boost.Wave: A Standard compliant C++ preprocessor library

    http://www.boost.org/

    Copyright (c) 2001-2012 Hartmut Kaiser. 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(FUNCTOR_INPUT_HPP_ED3A4C21_8F8A_453F_B438_08214FAC106A_INCLUDED)
#define FUNCTOR_INPUT_HPP_ED3A4C21_8F8A_453F_B438_08214FAC106A_INCLUDED

#include <boost/assert.hpp>
#include <boost/spirit/include/classic_multi_pass.hpp>
#include <boost/wave/wave_config.hpp>

// this must occur after all of the includes and before any code appears
#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_PREFIX
#endif

///////////////////////////////////////////////////////////////////////////////
namespace boost {
namespace wave {
namespace util {

///////////////////////////////////////////////////////////////////////////////
// 
//  class functor_input
// 
//      Implementation of the InputPolicy used by multi_pass 
//      functor_input gets tokens from a functor
//      Note: the functor must have a typedef for result_type
//            It also must have a static variable of type result_type defined 
//            to represent eof that is called eof.
//
//      This functor input policy template is essentially the same as the 
//      predefined multi_pass functor_input policy. The difference is, 
//      that the first token is not read at initialization time, but only 
//      just before returning the first token. Additionally it does not
//      call operator new() twice but only once.
//
///////////////////////////////////////////////////////////////////////////////
struct functor_input {

    template <typename FunctorT>
    class inner {
    private:
        typedef typename FunctorT::result_type result_type;

    public:
        typedef result_type value_type;

    private:
        struct Data {
            Data(FunctorT const &ftor_) 
            :   ftor(ftor_), was_initialized(false)
            {}
            
            FunctorT ftor;
            value_type curtok;
            bool was_initialized;
        };

       // Needed by compilers not implementing the resolution to DR45. For
       // reference, see
       // http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#45.

       friend struct Data;

    public:
        typedef std::ptrdiff_t difference_type;
        typedef result_type *pointer;
        typedef result_type &reference;

    protected:
        inner()
        :   data(0)
        {}

        inner(FunctorT const &x)
        :   data(new Data(x))
        {}

        inner(inner const &x)
        :   data(x.data)
        {}

        void destroy()
        {
            delete data;
            data = 0;
        }

        bool same_input(inner const &x) const
        {
            return data == x.data;
        }

        void swap(inner &x)
        {
            boost::spirit::classic::impl::mp_swap(data, x.data);
        }

        void ensure_initialized() const
        {
            if (data && !data->was_initialized) {
                data->curtok = (data->ftor)();    // get the first token
                data->was_initialized = true;
            }
        }

    public:
        reference get_input() const
        {
            ensure_initialized();
            return data->curtok;
        }

        void advance_input()
        {
            BOOST_ASSERT(0 != data);
            data->curtok = (data->ftor)();
            data->was_initialized = true;
        }

        bool input_at_eof() const
        {
            ensure_initialized();
            return !data || data->curtok == data->ftor.eof;
        }

        FunctorT& get_functor() const
        {
            BOOST_ASSERT(0 != data);
            return data->ftor;
        }

    private:
        mutable Data *data;
    };
};

///////////////////////////////////////////////////////////////////////////////
}   // namespace util
}   // namespace wave
}   // namespace boost 

// the suffix header occurs after all of the code
#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif

#endif // !defined(FUNCTOR_INPUT_HPP_ED3A4C21_8F8A_453F_B438_08214FAC106A_INCLUDED)
