// ----------------------------------------------------------------------------
// Copyright (C) 2002-2006 Marcin Kalicinski
//
// 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)
//
// For more information, see www.boost.org
// ----------------------------------------------------------------------------
#ifndef BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_READ_HPP_INCLUDED
#define BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_READ_HPP_INCLUDED

//#define BOOST_SPIRIT_DEBUG

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/detail/ptree_utils.hpp>
#include <boost/property_tree/detail/json_parser_error.hpp>
#include <boost/spirit/include/classic.hpp>
#include <boost/limits.hpp>
#include <string>
#include <locale>
#include <istream>
#include <vector>
#include <algorithm>

namespace boost { namespace property_tree { namespace json_parser
{

    ///////////////////////////////////////////////////////////////////////
    // Json parser context
        
    template<class Ptree>
    struct context
    {

        typedef typename Ptree::key_type::value_type Ch;
        typedef std::basic_string<Ch> Str;
        typedef typename std::vector<Ch>::iterator It;
        
        Str string;
        Str name;
        Ptree root;
        std::vector<Ptree *> stack;

        struct a_object_s
        {
            context &c;
            a_object_s(context &c): c(c) { }
            void operator()(Ch) const
            {
                if (c.stack.empty())
                    c.stack.push_back(&c.root);
                else
                {
                    Ptree *parent = c.stack.back();
                    Ptree *child = &parent->push_back(std::make_pair(c.name, Ptree()))->second;
                    c.stack.push_back(child);
                    c.name.clear();
                }
            }
        };
        
        struct a_object_e
        {
            context &c;
            a_object_e(context &c): c(c) { }
            void operator()(Ch) const
            {
                BOOST_ASSERT(c.stack.size() >= 1);
                c.stack.pop_back();
            }
        };

        struct a_name
        {
            context &c;
            a_name(context &c): c(c) { }
            void operator()(It, It) const
            {
                c.name.swap(c.string);
                c.string.clear();
            }
        };

        struct a_string_val
        {
            context &c;
            a_string_val(context &c): c(c) { }
            void operator()(It, It) const
            {
                BOOST_ASSERT(c.stack.size() >= 1);
                c.stack.back()->push_back(std::make_pair(c.name, Ptree(c.string)));
                c.name.clear();
                c.string.clear();
            }
        };

        struct a_literal_val
        {
            context &c;
            a_literal_val(context &c): c(c) { }
            void operator()(It b, It e) const
            {
                BOOST_ASSERT(c.stack.size() >= 1);
                c.stack.back()->push_back(std::make_pair(c.name, Str(b, e)));
                c.name.clear();
                c.string.clear();
            }
        };

        struct a_char
        {
            context &c;
            a_char(context &c): c(c) { }
            void operator()(It b, It e) const
            {
                c.string += *b;
            }
        };

        struct a_escape
        {
            context &c;
            a_escape(context &c): c(c) { }
            void operator()(Ch ch) const
            {
                switch (ch)
                {
                    case Ch('\"'): c.string += Ch('\"'); break;
                    case Ch('\\'): c.string += Ch('\\'); break;
                    case Ch('0'): c.string += Ch('\0'); break;
                    case Ch('b'): c.string += Ch('\b'); break;
                    case Ch('f'): c.string += Ch('\f'); break;
                    case Ch('n'): c.string += Ch('\n'); break;
                    case Ch('r'): c.string += Ch('\r'); break;
                    case Ch('t'): c.string += Ch('\t'); break;
                    default: BOOST_ASSERT(0);
                }
            }
        };

        struct a_unicode
        {
            context &c;
            a_unicode(context &c): c(c) { }
            void operator()(unsigned long u) const
            {
                u = (std::min)(u, static_cast<unsigned long>((std::numeric_limits<Ch>::max)()));
                c.string += Ch(u);
            }
        };

    };

    ///////////////////////////////////////////////////////////////////////
    // Json grammar

    template<class Ptree>
    struct json_grammar :
        public boost::spirit::classic::grammar<json_grammar<Ptree> >
    {
        
        typedef context<Ptree> Context;
        typedef typename Ptree::key_type::value_type Ch;

        mutable Context c;
        
        template<class Scanner>
        struct definition
        {
            
            boost::spirit::classic::rule<Scanner>
                root, object, member, array, item, value, string, number;
            boost::spirit::classic::rule<
                typename boost::spirit::classic::lexeme_scanner<Scanner>::type>
                character, escape;

            definition(const json_grammar &self)
            {

                using namespace boost::spirit::classic;

                // Assertions
                assertion<std::string> expect_object("expected object");
                assertion<std::string> expect_eoi("expected end of input");
                assertion<std::string> expect_objclose("expected ',' or '}'");
                assertion<std::string> expect_arrclose("expected ',' or ']'");
                assertion<std::string> expect_name("expected object name");
                assertion<std::string> expect_colon("expected ':'");
                assertion<std::string> expect_value("expected value");
                assertion<std::string> expect_escape("invalid escape sequence");

                // JSON grammar rules
                root 
                    =   expect_object(object) 
                        >> expect_eoi(end_p)
                        ;
                
                object 
                    =   ch_p('{')[typename Context::a_object_s(self.c)]
                        >> (ch_p('}')[typename Context::a_object_e(self.c)] 
                           | (list_p(member, ch_p(','))
                              >> expect_objclose(ch_p('}')[typename Context::a_object_e(self.c)])
                             )
                           )
                        ;
                
                member 
                    =   expect_name(string[typename Context::a_name(self.c)]) 
                        >> expect_colon(ch_p(':')) 
                        >> expect_value(value)
                        ;
                
                array 
                    =   ch_p('[')[typename Context::a_object_s(self.c)]
                        >> (ch_p(']')[typename Context::a_object_e(self.c)] 
                            | (list_p(item, ch_p(','))
                               >> expect_arrclose(ch_p(']')[typename Context::a_object_e(self.c)])
                              )
                           )
                    ;

                item 
                    =   expect_value(value)
                        ;

                value 
                    =   string[typename Context::a_string_val(self.c)] 
                        | (number | str_p("true") | "false" | "null")[typename Context::a_literal_val(self.c)]
                        | object 
                        | array
                        ;
                
                number 
                    =   strict_real_p 
                        | int_p
                        ;
                
                string 
                    =   +(lexeme_d[confix_p('\"', *character, '\"')])
                        ;
                
                character 
                    =   (anychar_p - "\\" - "\"")[typename Context::a_char(self.c)] 
                        | ch_p("\\") >> expect_escape(escape)
                        ;
                
                escape 
                    =   chset_p(detail::widen<Ch>("\"\\0bfnrt").c_str())[typename Context::a_escape(self.c)] 
                        | 'u' >> uint_parser<unsigned long, 16, 4, 4>()[typename Context::a_unicode(self.c)]
                        ;
                
                // Debug
                BOOST_SPIRIT_DEBUG_RULE(root);
                BOOST_SPIRIT_DEBUG_RULE(object);
                BOOST_SPIRIT_DEBUG_RULE(member);
                BOOST_SPIRIT_DEBUG_RULE(array);
                BOOST_SPIRIT_DEBUG_RULE(item);
                BOOST_SPIRIT_DEBUG_RULE(value);
                BOOST_SPIRIT_DEBUG_RULE(string);
                BOOST_SPIRIT_DEBUG_RULE(number);
                BOOST_SPIRIT_DEBUG_RULE(escape);
                BOOST_SPIRIT_DEBUG_RULE(character);

            }

            const boost::spirit::classic::rule<Scanner> &start() const
            {
                return root;
            }

        };

    };

    template<class It, class Ch>
    unsigned long count_lines(It begin, It end)
    {
        return static_cast<unsigned long>(std::count(begin, end, Ch('\n')) + 1);
    }

    template<class Ptree>
    void read_json_internal(std::basic_istream<typename Ptree::key_type::value_type> &stream,
                            Ptree &pt,
                            const std::string &filename)
    {

        using namespace boost::spirit::classic;
        typedef typename Ptree::key_type::value_type Ch;
        typedef typename std::vector<Ch>::iterator It;

        // Load data into vector
        std::vector<Ch> v(std::istreambuf_iterator<Ch>(stream.rdbuf()),
                          std::istreambuf_iterator<Ch>());
        if (!stream.good())
            BOOST_PROPERTY_TREE_THROW(json_parser_error("read error", filename, 0));
        
        // Prepare grammar
        json_grammar<Ptree> g;

        // Parse
        try
        {
            parse_info<It> pi = parse(v.begin(), v.end(), g, 
                                      space_p | comment_p("//") | comment_p("/*", "*/"));
            if (!pi.hit || !pi.full)
                BOOST_PROPERTY_TREE_THROW((parser_error<std::string, It>(v.begin(), "syntax error")));
        }
        catch (parser_error<std::string, It> &e)
        {
            BOOST_PROPERTY_TREE_THROW(json_parser_error(e.descriptor, filename, count_lines<It, Ch>(v.begin(), e.where)));
        }

        // Swap grammar context root and pt
        pt.swap(g.c.root);

    }

} } }

#endif
