// (C) Copyright Jonathan Turkanis 2005.
// 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.)

// See http://www.boost.org/libs/iostreams for documentation.

// Adapted from an example of James Kanze, with suggestions from Peter Dimov.
// See http://www.gabi-soft.fr/codebase-en.html.

#ifndef BOOST_IOSTREAMS_FINITE_STATE_FILTER_HPP_INCLUDED
#define BOOST_IOSTREAMS_FINITE_STATE_FILTER_HPP_INCLUDED

#include <cassert>
#include <cstdio>    // EOF.
#include <iostream>  // cin, cout.
#include <locale>
#include <string>
#include <boost/config.hpp>                 // JOIN, member template friends.
#include <boost/detail/workaround.hpp>
#include <boost/iostreams/categories.hpp>
#include <boost/iostreams/char_traits.hpp>
#include <boost/iostreams/checked_operations.hpp>   // put_if.
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/detail/ios.hpp>           // openmode.
#include <boost/iostreams/filter/stdio.hpp>
#include <boost/iostreams/operations.hpp>
#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/preprocessor/control/expr_if.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_base_and_derived.hpp>

namespace boost { namespace iostreams {

//------------------Definition of basic character classes---------------------//

struct finite_state_machine_base {

    static const int initial_state = 0;

        // All-inclusive character class.

    struct is_any {
        template<typename Ch>
        static bool test(Ch, const std::locale&) { return true; }
    };

        // Locale-sensitive character classes.

    #define BOOST_IOSTREAMS_CHARACTER_CLASS(class) \
        struct BOOST_JOIN(is_, class) { \
            template<typename Ch> \
            static bool test(Ch event, const std::locale& loc) \
            { return std::BOOST_JOIN(is, class)(event, loc); } \
        }; \
        /**/

    BOOST_IOSTREAMS_CHARACTER_CLASS(alnum)
    BOOST_IOSTREAMS_CHARACTER_CLASS(alpha)
    BOOST_IOSTREAMS_CHARACTER_CLASS(cntrl)
    BOOST_IOSTREAMS_CHARACTER_CLASS(digit)
    BOOST_IOSTREAMS_CHARACTER_CLASS(graph)
    BOOST_IOSTREAMS_CHARACTER_CLASS(lower)
    BOOST_IOSTREAMS_CHARACTER_CLASS(print)
    BOOST_IOSTREAMS_CHARACTER_CLASS(punct)
    BOOST_IOSTREAMS_CHARACTER_CLASS(space)
    BOOST_IOSTREAMS_CHARACTER_CLASS(upper)
    BOOST_IOSTREAMS_CHARACTER_CLASS(xdigit)

    #undef BOOST_IOSTREAMS_CHARACTER_CLASS
};

template<typename Ch>
struct finite_state_machine_base_ex : finite_state_machine_base {
    template<Ch C>
    struct is {
        static bool test(Ch event, const std::locale&)
        {
            return event == C;
        }
    };
};

//------------------Definition of base class for finite state filters---------//

namespace detail {

template<typename FiniteStateMachine>
class finite_state_filter_impl;

} // End namespace detail.

template<typename Derived, typename Ch = char>
class finite_state_machine : public finite_state_machine_base_ex<Ch> {
public:
    typedef Ch                                  char_type;
    typedef typename char_traits<Ch>::int_type  int_type;
    void imbue(const std::locale& loc) { loc_ = loc; }
    const std::locale& getloc() const { return loc_; }
protected:
    finite_state_machine() : off_(0) { }

    // Template whose instantiations make up transition table.

    template< int State,
              typename CharacterClass,
              int NextState,
              void (Derived::*Action)(char_type) >
    struct row {
        typedef CharacterClass  character_class;
        static const int        state = State;
        static const int        next_state = NextState;
        static void execute(Derived& d, char_type event)
        {
            (d.*Action)(event);
        }
    };

    // Stack interface.

    bool empty() const
    {
        return off_ == buf_.size();
    }
    void push(char c) { buf_ += c; }
    char_type pop()
    {
        char_type result = buf_[off_++];
        if (off_ == buf_.size())
            clear();
        return result;
    }
    char_type& top() { return buf_[off_]; }
    void clear()
    {
        buf_.clear();
        off_ = 0;
    }

    // Default event handlers.

    void on_eof() { }
    void skip(char_type) { }

#if BOOST_WORKAROUND(__MWERKS__, <= 0x3206)
    template<typename Ch>
    void _push_impl(Ch c) { push(c); }
#endif

#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
    template<typename FiniteStateFilter>
    friend class detail::finite_state_filter_impl;
#else
    public:
#endif
    void on_any(char_type) { }
private:
    typedef std::basic_string<char_type>     string_type;
    typedef typename string_type::size_type  size_type;
    std::locale  loc_;
    string_type  buf_;
    size_type    off_;
};

#if !BOOST_WORKAROUND(__MWERKS__, <= 0x3206)
# define BOOST_IOSTREAMS_FSM(fsm) \
    template<typename Ch> \
    void push(Ch c) \
    { ::boost::iostreams::finite_state_machine<fsm, Ch>::push(c); } \
    template<typename Ch> \
    void skip(Ch c) { (void) c; } \
    /**/
#else // #ifndef __MWERKS__
# define BOOST_IOSTREAMS_FSM(fsm) \
    void push(char c) { this->_push_impl(c); } \
    void push(wchar_t c) { this->_push_impl(c); } \
    void skip(char c) { (void) c; } \
    void skip(wchar_t c) { (void) c; } \
    /**/
#endif

//------------------Definition of finite_state_filter_impl--------------------//

namespace detail {

template<typename FiniteStateMachine>
class finite_state_filter_impl : protected FiniteStateMachine
{
private:
    template<typename First, typename Last>
    struct process_event_impl;
public:
    typedef typename char_type_of<FiniteStateMachine>::type char_type;

    finite_state_filter_impl() : state_(FiniteStateMachine::initial_state) { }

    template<typename T0>
    explicit finite_state_filter_impl(const T0& t0)
        : FiniteStateMachine(t0), state_(FiniteStateMachine::initial_state)
        { }

    template<typename T0, typename T1>
    finite_state_filter_impl(const T0& t0, const T1& t1)
        : FiniteStateMachine(t0, t1), state_(FiniteStateMachine::initial_state)
        { }

    template<typename T0, typename T1, typename T2>
    finite_state_filter_impl(const T0& t0, const T1& t1, const T2& t2)
        : FiniteStateMachine(t0, t1, t2),
          state_(FiniteStateMachine::initial_state)
        { }
protected:
    void process_event(char_type c)
    {
        typedef typename FiniteStateMachine::transition_table  transitions;
        typedef typename mpl::begin<transitions>::type         first;
        typedef typename mpl::end<transitions>::type           last;
        state_ = process_event_impl<first, last>::execute(*this, state_, c);
    }
    int& state() { return state_; }
    void reset()
    {
        state_ = FiniteStateMachine::initial_state;
        this->clear();
    }
private:
    template<typename First, typename Last>
    struct process_event_impl {
        static int execute(FiniteStateMachine& fsm, int state, char_type event)
        {
            typedef typename mpl::deref<First>::type  rule;
            typedef typename mpl::next<First>::type   next;
            typedef typename rule::character_class    character_class;

            if ( state == rule::state &&
                 character_class::test(event, fsm.getloc()) )
            {
                // Rule applies.
                rule::execute(fsm, event);
                return rule::next_state;
            }

            // Rule is inapplicable: try next rule.
            return process_event_impl<next, Last>::execute(fsm, state, event);
        }
    };

    template<typename Last>
    struct process_event_impl<Last, Last> {
        static int execute(FiniteStateMachine& fsm, int state, char_type c)
        {
            on_any(fsm, c);
            return state;
        }
    };
#if BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042)) // Tru64.
    public:
#endif
    template<typename FSM>
    static void on_any(FSM& fsm, char_type c) { fsm.on_any(c); }
private:
    int state_;
};

} // End namespace detail.

//------------------Definition of finite_state_filter-------------------------//

template<typename FiniteStateMachine>
class finite_state_filter
    : public detail::finite_state_filter_impl<FiniteStateMachine>
{
private:
    typedef detail::finite_state_filter_impl<FiniteStateMachine>  base_type;
public:
    typedef typename base_type::char_type                         char_type;
    typedef char_traits<char_type>                                traits_type;
    typedef typename base_type::int_type                          int_type;
    struct category
        : dual_use, filter_tag, closable_tag, localizable_tag
        { };

    finite_state_filter() : flags_(0) { }

    template<typename T0>
    finite_state_filter(const T0& t0)
        : base_type(t0), flags_(0)
        { }

    template<typename T0, typename T1>
    finite_state_filter(const T0& t0, const T1& t1)
        : base_type(t0, t1), flags_(0)
        { }

    template<typename T0, typename T1, typename T2>
    finite_state_filter(const T0& t0, const T1& t1, const T2& t2)
        : base_type(t0, t1, t2), flags_(0)
        { }

    template<typename Source>
    int_type get(Source& src)
    {
        assert((flags_ & f_write) == 0);
        flags_ |= f_read;

        while (true) {
            if ((flags_ & f_eof) == 0) {

                // Read a character and process it.
                int_type c;
                if (traits_type::is_eof(c = iostreams::get(src))) {
                    flags_ |= f_eof;
                    this->on_eof();
                } else if (!traits_type::would_block(c)) {
                    this->process_event(c);
                }
            }

            // Return a character, if available.
            if (!this->empty())
                return this->pop();
            else if ((flags_ & f_eof) != 0)
                return traits_type::eof();
        }
    }

    template<typename Sink>
    bool put(Sink& dest, char_type c)
    {
        assert((flags_ & f_read) == 0);
        flags_ |= f_write;

        this->process_event(c);
        while (!this->empty() && iostreams::put(dest, this->top()))
            this->pop();

        return true;
    }

    template<typename Device>
    void close(Device& dev, BOOST_IOS::openmode which)
    {
        if (which == BOOST_IOS::out) {
            while (!this->empty())
                iostreams::put_if(dev, this->pop());
        }
        this->reset();
        flags_ = 0;
    }
private:
    enum flags {
        f_read    = 1,
        f_write   = f_read << 1,
        f_eof     = f_write << 1
    };

    int flags_;
};

} }       // End namespaces iostreams, boost.

#endif // #ifndef BOOST_IOSTREAMS_FINITE_STATE_FILTER_HPP_INCLUDED
