#ifndef BOOST_NETWORK_PROTOCOL_HTTP_SERVER_REQUEST_PARSER_HPP_20101005
#define BOOST_NETWORK_PROTOCOL_HTTP_SERVER_REQUEST_PARSER_HPP_20101005

// Copyright 2010 Dean Michael Berris. 
// 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)

#include <utility>
#include <boost/range/iterator_range.hpp>
#include <boost/logic/tribool.hpp>
#include <boost/fusion/tuple.hpp>
#include <boost/algorithm/string/classification.hpp>

namespace boost { namespace network { namespace http {

    template <class Tag>
    struct request_parser {

        enum state_t {
            method_start
            , method_char
            , method_done
            , uri_char
            , uri_done
            , version_h
            , version_t1
            , version_t2
            , version_p
            , version_slash
            , version_d1
            , version_dot
            , version_d2
            , version_cr
            , version_done
            , header_name
            , header_colon
            , header_value
            , header_cr
            , header_line_done
            , headers_cr
            , headers_done
        };

        explicit request_parser(state_t start_state = method_start)
        : internal_state(start_state)
        {}

        void reset(state_t start_state = method_start) {
            internal_state = method_start;
        }

        state_t state() const { return internal_state; }

        template <class Range>
        fusion::tuple<logic::tribool, iterator_range<typename Range::const_iterator> >
        parse_until(state_t stop_state, Range & range) {
            logic::tribool parsed_ok = logic::indeterminate;
            typedef typename range_iterator<Range>::type iterator;
            iterator start = boost::begin(range)
                    , end  = boost::end(range)
                    , current_iterator = start;
            iterator_range<iterator> local_range = 
                boost::make_iterator_range(start, end);
            while (
                !boost::empty(local_range)
                && stop_state != internal_state
                && indeterminate(parsed_ok)
            ) {
                current_iterator = boost::begin(local_range);
                switch(internal_state) {
                    case method_start:
                        if (algorithm::is_upper()(*current_iterator)) internal_state = method_char;
                        else parsed_ok = false;
                        break;
                    case method_char:
                        if (algorithm::is_upper()(*current_iterator)) break;
                        else if (algorithm::is_space()(*current_iterator)) internal_state = method_done;
                        else parsed_ok = false;
                        break;
                    case method_done:
                        if (algorithm::is_cntrl()(*current_iterator)) parsed_ok = false;
                        else if (algorithm::is_space()(*current_iterator)) parsed_ok = false;
                        else internal_state = uri_char;
                        break;
                    case uri_char:
                        if (algorithm::is_cntrl()(*current_iterator)) parsed_ok = false;
                        else if (algorithm::is_space()(*current_iterator)) internal_state = uri_done;
                        break;
                    case uri_done:
                        if (*current_iterator == 'H') internal_state = version_h;
                        else parsed_ok = false;
                        break;
                    case version_h:
                        if (*current_iterator == 'T') internal_state = version_t1;
                        else parsed_ok = false;
                        break;
                    case version_t1:
                        if (*current_iterator == 'T') internal_state = version_t2;
                        else parsed_ok = false;
                        break;
                    case version_t2:
                        if (*current_iterator == 'P') internal_state = version_p;
                        else parsed_ok = false;
                        break;
                    case version_p:
                        if (*current_iterator == '/') internal_state = version_slash;
                        else parsed_ok = false;
                        break;
                    case version_slash:
                        if (algorithm::is_digit()(*current_iterator)) internal_state = version_d1;
                        else parsed_ok = false;
                        break;
                    case version_d1:
                        if (*current_iterator == '.') internal_state = version_dot;
                        else parsed_ok = false;
                        break;
                    case version_dot:
                        if (algorithm::is_digit()(*current_iterator)) internal_state = version_d2;
                        else parsed_ok = false;
                        break;
                    case version_d2:
                        if (*current_iterator == '\r') internal_state = version_cr;
                        else parsed_ok = false;
                        break;
                    case version_cr:
                        if (*current_iterator == '\n') internal_state = version_done;
                        else parsed_ok = false;
                        break;
                    case version_done:
                        if (algorithm::is_alnum()(*current_iterator)) internal_state = header_name;
                        else if (*current_iterator == '\r') internal_state = headers_cr;
                        else parsed_ok = false;
                        break;
                    case header_name:
                        if (*current_iterator == ':') internal_state = header_colon;
                        else if (algorithm::is_alnum()(*current_iterator) || algorithm::is_punct()(*current_iterator)) break;
                        else parsed_ok = false;
                        break;
                    case header_colon:
                        if (*current_iterator == ' ') internal_state = header_value;
                        else parsed_ok = false;
                        break;
                    case header_value:
                        if (*current_iterator == '\r') internal_state = header_cr;
                        else if (algorithm::is_cntrl()(*current_iterator)) parsed_ok = false;
                        break;
                    case header_cr:
                        if (*current_iterator == '\n') internal_state = header_line_done;
                        else parsed_ok = false;
                        break;
                    case header_line_done:
                        if (*current_iterator == '\r') internal_state = headers_cr;
                        else if (algorithm::is_alnum()(*current_iterator)) internal_state = header_name;
                        else parsed_ok = false;
                        break;
                    case headers_cr:
                        if (*current_iterator == '\n') internal_state = headers_done;
                        else parsed_ok = false;
                        break;
                    case headers_done:
                        // anything that follows after headers_done is allowed.
                        break;
                    default:
                        parsed_ok = false;
                };
                if (internal_state == stop_state) parsed_ok = true;
                local_range = boost::make_iterator_range(
                    ++current_iterator, end);
            }
            return fusion::make_tuple(
                parsed_ok, 
                boost::make_iterator_range(
                    start, current_iterator
                )
                );
        }

    private:
        state_t internal_state;

    };
    
} /* http */
    
} /* network */
    
} /* boost */

#endif /* BOOST_NETWORK_PROTOCOL_HTTP_SERVER_REQUEST_PARSER_HPP_20101005 */
