/**
 * @file
 * reader.h
 */

/******************************************************************************
 *
 * This file has been directly derived from the json-cpp distribution from
 * www.json.org which has been dedicated to the Public Domain
 *
 ******************************************************************************/

/******************************************************************************
 * Copyright (c) 2012, AllSeen Alliance. All rights reserved.
 *
 *    Permission to use, copy, modify, and/or distribute this software for any
 *    purpose with or without fee is hereby granted, provided that the above
 *    copyright notice and this permission notice appear in all copies.
 *
 *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 ******************************************************************************/

#ifndef CPPTL_JSON_READER_H_INCLUDED
# define CPPTL_JSON_READER_H_INCLUDED

# include "json_features.h"
# include "value.h"
# include <deque>
# include <stack>
# include <string>

namespace Json {

/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a Value.
 *
 */
class JSON_API Reader {
  public:
    typedef char Char;
    typedef const Char*Location;

    /** \brief Constructs a Reader allowing all features
     * for parsing.
     */
    Reader();

    /** \brief Constructs a Reader allowing the specified feature set
     * for parsing.
     */
    Reader(const Features& features);

    /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document.
     * \param document UTF-8 encoded string containing the document to read.
     * \param root [out] Contains the root value of the document if it was
     *             successfully parsed.
     * \param collectComments \c true to collect comment and allow writing them back during
     *                        serialization, \c false to discard comments.
     *                        This parameter is ignored if Features::allowComments_
     *                        is \c false.
     * \return \c true if the document was successfully parsed, \c false if an error occurred.
     */
    bool parse(const std::string& document,
               Value& root,
               bool collectComments = true);

    /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document.
     * \param document UTF-8 encoded string containing the document to read.
     * \param root [out] Contains the root value of the document if it was
     *             successfully parsed.
     * \param collectComments \c true to collect comment and allow writing them back during
     *                        serialization, \c false to discard comments.
     *                        This parameter is ignored if Features::allowComments_
     *                        is \c false.
     * \return \c true if the document was successfully parsed, \c false if an error occurred.
     */
    bool parse(const char*beginDoc, const char*endDoc,
               Value& root,
               bool collectComments = true);

    /** \brief Returns a user friendly string that list errors in the parsed document.
     * \return Formatted error message with the list of errors with their location in
     *         the parsed document. An empty string is returned if no error occurred
     *         during parsing.
     */
    std::string getFormatedErrorMessages() const;

  private:
    enum TokenType {
        tokenEndOfStream = 0,
        tokenObjectBegin,
        tokenObjectEnd,
        tokenArrayBegin,
        tokenArrayEnd,
        tokenString,
        tokenNumber,
        tokenTrue,
        tokenFalse,
        tokenNull,
        tokenArraySeparator,
        tokenMemberSeparator,
        tokenComment,
        tokenError
    };

    class Token {
      public:
        TokenType type_;
        Location start_;
        Location end_;
    };

    class ErrorInfo {
      public:
        Token token_;
        std::string message_;
        Location extra_;
    };

    typedef std::deque<ErrorInfo> Errors;

    bool expectToken(TokenType type, Token& token, const char*message);
    bool readToken(Token& token);
    void skipSpaces();
    bool match(Location pattern,
               int patternLength);
    bool readComment();
    bool readCStyleComment();
    bool readCppStyleComment();
    bool readString();
    void readNumber();
    bool readValue();
    bool readObject(Token& token);
    bool readArray(Token& token);
    bool decodeNumber(Token& token);
    bool decodeString(Token& token);
    bool decodeString(Token& token, std::string& decoded);
    bool decodeDouble(Token& token);
    bool decodeUnicodeCodePoint(Token& token,
                                Location& current,
                                Location end,
                                unsigned int& unicode);
    bool decodeUnicodeEscapeSequence(Token& token,
                                     Location& current,
                                     Location end,
                                     unsigned int& unicode);
    bool addError(const std::string& message,
                  Token& token,
                  Location extra = 0);
    bool recoverFromError(TokenType skipUntilToken);
    bool addErrorAndRecover(const std::string& message,
                            Token& token,
                            TokenType skipUntilToken);
    void skipUntilSpace();
    Value& currentValue();
    Char getNextChar();
    void getLocationLineAndColumn(Location location,
                                  int& line,
                                  int& column) const;
    std::string getLocationLineAndColumn(Location location) const;
    void addComment(Location begin,
                    Location end,
                    CommentPlacement placement);
    void skipCommentTokens(Token& token);

    typedef std::stack<Value*> Nodes;
    Nodes nodes_;
    Errors errors_;
    std::string document_;
    Location begin_;
    Location end_;
    Location current_;
    Location lastValueEnd_;
    Value*lastValue_;
    std::string commentsBefore_;
    Features features_;
    bool collectComments_;
};

} // namespace Json

#endif // CPPTL_JSON_READER_H_INCLUDED
