// ==========================================================================
// Author:  Yee Hsu
// Date:    6/8/2011
//
// Desc:    Json Object Parser. A Json parser library that can be used
//          to parse Json Objects and return the objects for storage or
//          usage. 
// ==========================================================================

#include <stdafx.h>
#include "JsonObject.h"

#include <json/json_inttypes.h>
#include <json/json.h>
#include <exception>
#include <string.h>
#include <fstream>

//#include <boost/lexical_cast.hpp>
//#include "QLogger.h"

#if ((_MSC_VER) && (_MSC_VER >= 1300))
#    define strtoll _strtoi64 
#    define strtoull _strtoui64
#endif 

using namespace std;

namespace __ET__
{
    Json::Json()
        : m_json(0)
    {}

    Json::Json(Json const& json)
        : m_json(0)
    {
        if (!json.IsNull())
        {
            m_json = json_object_get(json.m_json);
        }
    }

    Json::~Json()
    {
        if (m_json)
        {
            json_object_put(m_json);
            m_json = NULL;
        }
    }

    Json::Json(json_object* json)
        : m_json(json)
    {}

    Json::Json(const char* sz)
        : m_json(0)
    {
        try 
        {
            if (sz)
                m_json = json_tokener_parse(sz);

            if (is_error(m_json))
            {
                //QV_ERROR("Error parse json from string: %s", sz);
                m_json = 0;
            }
        }
        catch (std::exception& ex)
        {
            //QV_ERROR("Exception caught: %s", ex.what());
        }
    }

    Json::Json(String const& str)
        : m_json(0)
    {
        try
        {
            if (!str.empty())
            {
                m_json = json_tokener_parse(str.c_str());
                if (is_error(m_json))
                {
                    //QV_ERROR("Error parse json from string: %s", str.c_str());
                    m_json = 0;
                }
            }
        }
        catch (std::exception& ex)
        {
            //QV_ERROR("Exception caught: %s", ex.what());
        }
    }

    Json& Json::operator=(Json const& json)
    {
        if (m_json == json.m_json)
            return (*this);

        if (m_json)
            json_object_put(m_json);

        if (json.m_json)
            m_json = json_object_get(json.m_json);

        return *this;
    }

    Json& Json::operator=(String const& str)
    {
        Json json(json_object_new_string(str.c_str()));
        return operator=(json);
    }

    Json& Json::operator=(int i)
    {
        Json json(json_object_new_int(i));
        return operator=(json);
    }

    Json& Json::operator=(float f)
    {
        Json json(json_object_new_double(f));
        return operator=(json);
    }

    Json& Json::operator=(double d)
    {
        Json json(json_object_new_double(d));
        return operator=(json);
    }

    Json& Json::operator=(bool b)
    {
        Json json(json_object_new_boolean(b));
        return operator=(json);
    }

    json_type Json::GetType() const
    {
        if (IsNull())
            return json_type_null;
        else
            return json_object_get_type(m_json);
    }

    bool Json::IsNull() const
    {
        return (m_json == NULL || m_json == (json_object*)-1);
    }

    bool Json::IsObject() const
    {
        if (!IsNull())
        {
            return (json_object_is_type(m_json, json_type_object) != 0);
        }
        else
        {
            return false;
        }
    }

    bool Json::IsArray() const
    {
        if (!IsNull())
        {
            return (json_object_is_type(m_json, json_type_array) != 0);
        }
        else
        {
            return false;
        }
    }
    bool Json::IsString() const
    {
        if (!IsNull())
        {
            return (json_object_is_type(m_json, json_type_string) != 0);
        }
        else
        {
            return false;
        }
    }
    bool Json::IsInt() const
    {
        if (!IsNull())
        {
            return (json_object_is_type(m_json, json_type_int) != 0);
        }
        else
        {
            return false;
        }
    }
    bool Json::IsDouble() const
    {
        if (!IsNull())
        {
            return (json_object_is_type(m_json, json_type_double) != 0);
        }
        else
        {
            return false;
        }
    }
    bool Json::IsBoolean() const
    {
        if (!IsNull())
        {
            return (json_object_is_type(m_json, json_type_boolean) != 0);
        }
        else
        {
            return false;
        }
    }

    String Json::ToString() const
    {
        const char* sz = NULL;

        if (!IsNull() && (sz = json_object_get_string(m_json)))
        {
            return String(sz);
        }
        else 
        {
            return "null";
        }
    }

    String Json::ToPrettyString() const
    {
        std::ostringstream out;

        ToPrettyStringEx(out);

        out << endl;

        return out.str();
    }

    void Json::ToPrettyStringEx(std::ostringstream& out, int level, int indent) const
    {        
        if (IsNull())
        {
            out << "null";
            return;
        }

        String sInd( level * indent, ' ' );
        String sInd1( (level + 1) * indent, ' ');

        if (IsObject())
        {
            out << "{";

            // for each key
            int cnt = 0;
            json_object_object_foreach(m_json, _key, _val)
            {
                // output key
                if (cnt++)
                {
                    out << ",";
                }
                out << "\n" << sInd1 << "\"" << _key << "\": ";

                Json child(json_object_get(_val));
                child.ToPrettyStringEx(out, level+1, indent);
            }
            if (cnt > 0)
                out << "\n" << sInd;

            out << "}";

            return;
        }

        else if (IsArray())
        {
            int len = json_object_array_length(m_json);
            out << "[";
            for (int i = 0; i < len; ++i) 
            {
                if (i)
                {
                    out << ",";
                }
                out << "\n" << sInd1;

                Json child(
                    json_object_get(json_object_array_get_idx(m_json, i))
                    );
                child.ToPrettyStringEx(out, level + 1, indent);
            }
            if (len > 0)
                out << "\n" << sInd;
            out << "]";

            return;
        }
        else 
        {
            out << json_object_to_json_string(m_json);
        }
    }

    bool Json::operator==(Json const& json) const
    {
        return (m_json == json.m_json);
    }

    bool Json::operator==(JsonObject const& obj) const
    {
        return operator==(obj.GetJson());
    }

    bool Json::operator==(JsonArray const& arr) const
    {
        return operator==(arr.GetJson());
    }

    bool Json::operator==(String const& str) const
    {
        if (m_json && json_object_get_type(m_json) == json_type_string)
        {
            const char* sz = json_object_get_string(m_json);
            if (sz)
            {
                return (str == json_object_get_string(m_json));
            }
        }
        return false;
    }

    bool Json::operator==(int i) const
    {
        if (m_json && json_object_get_type(m_json) == json_type_int)
        {
            return (i == json_object_get_int(m_json));
        }
        return false;
    }

    bool Json::operator==(float f) const
    {
        if (m_json && json_object_get_type(m_json) == json_type_double)
        {
            return (f == json_object_get_double(m_json));
        }
        return false;
    }

    bool Json::operator==(double d) const
    {
        if (m_json && json_object_get_type(m_json) == json_type_double)
        {
            return (d == json_object_get_double(m_json));
        }
        return false;
    }

    bool Json::operator==(bool val) const
    {
        bool bRet = false;
        if (m_json)
        {
            switch (json_object_get_type(m_json))
            {
            case json_type_boolean:
                bRet = (val == (json_object_get_boolean(m_json) == TRUE));
                break;
            case json_type_int:
                bRet = (val == (0 != json_object_get_int(m_json)));
                break;
                // try to convert string type to boolean smartly
            case json_type_string:
                {
                    const char* sz = json_object_get_string(m_json);
                    if (sz)
                        bRet = (strcmp(sz, "true") == 0);
                    break;
                }
            default:
                bRet = false;
                break;
            }
        }
        return bRet;
    }
    /*
    bool Json::Load(String const& file)
    {
        if (file.empty() || !boost::filesystem::exists(file)) return false;

        json_object* json = json_object_from_file(file.c_str());
        if (json == 0 || is_error(json))
        {
            QV_ERROR("Error load json from: %s", file.c_str());
            return false;
        }

        *this = Json(json);
        return true;
    }*/
    bool Json::Save(String const& file)
    {
        if (file.empty()) return false;

        std::ofstream f(file.c_str());
        if (f)
        {
            f << ToPrettyString();
            f.flush();
            f.close();

            return true;
        }
        else 
        {
            return false;
        }
    }

    //
    // 
    JsonArray::JsonArray()
        : m_json(json_object_new_array())
    {}
    JsonArray::JsonArray(JsonArray const& arr)
        : m_json(arr.m_json)
    {}
    JsonArray::JsonArray(Json const& json)
        : m_json(json_object_new_array())
    {
        if (json.IsArray())
        {
            m_json = json;
        }
    }
    JsonArray::JsonArray(std::vector<Json> const& vec)
        : m_json(json_object_new_array())
    {
        std::vector<Json>::const_iterator citer;
        for (citer = vec.begin(); citer != vec.end(); ++citer)
        {
            Add(*citer);
        }
    }
    JsonArray::~JsonArray()
    {}

    size_t JsonArray::Size() const
    {
        return json_object_array_length(m_json.m_json);
    }

    json_type JsonArray::GetType(size_t idx) const
    {
        json_object* item =    GetJsonObject(idx);
        QV_ASSERT(item);

        return json_object_get_type(item);
    }

    json_object* JsonArray::GetJsonObject(size_t idx) const
    {
        QV_ASSERT(m_json.m_json);
        return json_object_array_get_idx(m_json.m_json, idx);
    }

    bool JsonArray::Get(size_t idx, int32_t& L) const
    {
        json_object* json = GetJsonObject(idx);
        if (!json) return false;

        switch (json_object_get_type(json))
        {
        case json_type_int:
            L = json_object_get_int(json);
            return true;
            break;
        /*
        case json_type_int64:
            L = (int32_t)json_object_get_int64(json);
            return true;
            break;
        */
        case json_type_string:
            {
                const char* sz = json_object_get_string(json);
                if (sz)
                {
                    L = strtol(sz, NULL, 10);
                    return true;
                }
                break;
            }
        default:
            break;
        }
        return false;
    }

    bool JsonArray::Get(size_t idx, uint32_t& L) const
    {
        json_object* json = GetJsonObject(idx);
        if (!json) return false;

        switch (json_object_get_type(json))
        {
        case json_type_int:
            {
                L = (uint32_t)json_object_get_int(json);
                return true;
            }
        /*
        case json_type_int64:
            {
                L = (uint32_t)json_object_get_int64(json);
                return true;
            }
        */
        case json_type_string:
            {
                const char* sz = json_object_get_string(json);
                if (sz)
                {
                    L = strtoul(sz, NULL, 10);
                    return true;
                }
                break;
            }
        default:
            break;
        }
        return false;
    }

    bool JsonArray::Get(size_t idx, int64_t& val) const
    {
        Get(idx, (int32_t&) val);
    }

    /*
    bool JsonArray::Get(size_t idx, int64_t& val) const
    {
        json_object* json = GetJsonObject(idx);
        if (json && json_object_is_type(json, json_type_int64))
        {
            val = json_object_get_int64(json);
            return true;
        }
        else
        {
            return false;
        }
    }

    bool JsonArray::Get(size_t idx, uint64_t& val) const
    {
        json_object* json = GetJsonObject(idx);
        if (json && json_object_is_type(json, json_type_int64))
        {
            val = (uint64_t)json_object_get_int64(json);
            return true;
        }
        else
        {
            return false;
        }
    }
    */

    bool JsonArray::Get(size_t idx, long& L) const
    {
        if (sizeof(long) == sizeof(int64_t))
        {
            return Get(idx, (int64_t&)L);
        }
        else
        {
            return Get(idx, (int32_t&)L);
        }
    }

    bool JsonArray::Get(size_t idx, double& d) const
    {
        json_object* json = GetJsonObject(idx);
        if (json && json_object_is_type(json, json_type_double))
        {
            d = json_object_get_double(json);
            return true;
        }
        else
        {
            return false;
        }
    }

    bool JsonArray::Get(size_t idx, bool& b) const
    {
        json_object* json = GetJsonObject(idx);
        if (json && json_object_is_type(json, json_type_boolean))
        {
            b = (json_object_get_boolean(json) == TRUE);
            return true;
        }
        else
        {
            return false;
        }
    }

    bool JsonArray::Get(size_t idx, String& str) const
    {
        json_object* json = GetJsonObject(idx);
        if (json && json_object_is_type(json, json_type_string))
        {
            const char* sz = json_object_get_string(json);
            if (sz)
            {
                str.assign(sz);
                return true;
            }
        }
        return false;
    }

    bool JsonArray::Get(size_t idx, Json& json) const
    {
        json_object* tmp = GetJsonObject(idx);
        if (tmp)
        {
            json = Json(Json(json_object_get(tmp)));
            return true;
        }
        else
        {
            return false;
        }
    }
    bool JsonArray::Get(size_t idx, JsonArray& arr) const
    {
        json_object* json = GetJsonObject(idx);
        if (json && json_object_is_type(json, json_type_array))
        {
            arr = JsonArray(Json(json_object_get(json)));
            return true;
        }
        else
        {
            return false;
        }
    }
    bool JsonArray::Get(size_t idx, JsonObject& obj) const
    {
        json_object* json = GetJsonObject(idx);
        if (json && json_object_is_type(json, json_type_object))
        {
            obj = JsonObject(Json(json_object_get(json)));
            return true;
        }
        else
        {
            return false;
        }
    }
#ifdef __GNUC__
    bool JsonArray::Get(size_t idx, size_t& val) const
    {
        json_object* json = GetJsonObject(idx);
        if (!json) return false;

        switch (json_object_get_type(json))
        {
        case json_type_int:
            {
                val = (size_t)json_object_get_int(json);
                return true;
            }
        case json_type_int64:
            {
                val = (size_t)json_object_get_int64(json);
                return true;
            }
        case json_type_string:
            {
                const char* sz = json_object_get_string(json);
                if (sz)
                {
                    val = (size_t)strtoull(sz, NULL, 10);
                    return true;
                }
                break;
            }
        default:
            break;
        }
        return false;
    }
#endif

    bool JsonArray::Add(Json const& json)
    {
        if (json.m_json)
        {
            json_object* obj = json_object_get(json.m_json);
            json_object_array_add(m_json.m_json, obj);
            return true;
        }
        else
        {
            return false;
        }
    }

    bool JsonArray::Add(int32_t val)
    {
        json_object* obj = json_object_new_int(val);
        QV_ASSERT(obj);

        json_object_array_add(m_json.m_json, obj);
        return true;
    }

    bool JsonArray::Add(uint32_t val)
    {
        int32_t i = (int32_t)val;

        if (i < 0)
        {
            return Add((uint64_t)val);
        }
        else
        {
            return Add(i);
        }
    }

    /*
    bool JsonArray::Add(int64_t val)
    {
        json_object* obj = json_object_new_int64(val);
        QV_ASSERT(obj);

        json_object_array_add(m_json.m_json, obj);
        return true;
    }

    bool JsonArray::Add(uint64_t val)
    {
        int64_t i = (int64_t)val;
        if (i < 0)
        {
            return Add(boost::lexical_cast<String>(val));
        }
        else
        {
            return Add(i);
        }
    }
    */

    bool JsonArray::Add(double d)
    {
        json_object* obj = json_object_new_double(d);
        QV_ASSERT(obj);

        json_object_array_add(m_json.m_json, obj);
        return true;
    }
    bool JsonArray::Add(bool b)
    {
        json_object* obj = json_object_new_boolean((boolean)b);
        QV_ASSERT(obj);

        json_object_array_add(m_json.m_json, obj);
        return true;
    }
    bool JsonArray::Add(String const& str)
    {
        return Add(str.c_str());
    }
    bool JsonArray::Add(const char* sz, int len)
    {
        json_object* obj = NULL;
        if (len > 0)
        {
            obj = json_object_new_string_len(sz, len);
        }
        else
        {
            obj = json_object_new_string(sz);
        }
        QV_ASSERT(obj);

        json_object_array_add(m_json.m_json, obj);
        return true;
    }

    bool JsonArray::Add(JsonObject const& obj)
    {
        return Add(obj.ToJson());
    }
    bool JsonArray::Add(JsonArray const& arr)
    {
        return Add(arr.m_json);
    }

    Json const& JsonArray::GetJson() const
    {
        return m_json;
    }

    Json& JsonArray::GetJson()
    {
        return m_json;
    }

    Json JsonArray::ToJson() const
    {
        return m_json;
    }

    String JsonArray::ToString() const
    {
        return m_json.ToString();
    }

    String JsonArray::ToPrettyString() const
    {
        ostringstream out;

        m_json.ToPrettyStringEx(out);

        out << endl;

        return out.str();
    }

    JsonArray& JsonArray::operator=(JsonArray const& arr)
    {
        m_json = arr.m_json;

        return *this;
    }

    Json JsonArray::operator[](size_t idx) const
    {
        QV_ASSERT(!m_json.IsNull() && m_json.IsArray());

        if (idx >= Size())
        {
            throw out_of_range("JsonArray out of range!");
        }

        Json tmp;
        Get(idx, tmp);

        return tmp;
    }




    //---------------------------------------------------------------
    // JsonObject
    //---------------------------------------------------------------
    JsonObject::JsonObject()
        : m_json(json_object_new_object())
    {}

    JsonObject::JsonObject(Json const& json)
        : m_json(json_object_new_object())
    {
        if (json.IsObject())
        {
            m_json = json;
        }
    }

    JsonObject::JsonObject(JsonObject const& obj)
        : m_json(obj.m_json)
    {}

    JsonObject::~JsonObject()
    {}

    json_object* JsonObject::GetJsonObject(String const& key) const
    {
        return json_object_object_get(m_json.m_json, key.c_str());
    }

    bool JsonObject::HasKey(String const& key) const
    {
        return (GetJsonObject(key) != NULL);
    }

    void JsonObject::Remove(String const& key)
    {
        json_object_object_del(m_json.m_json, key.c_str());
    }

    json_type JsonObject::GetType(String const& key) const
    {
        Json json;
        if (HasKey(key) && Get(key, json))
        {
            return json.GetType();
        }
        return json_type_null;
    }

    bool JsonObject::Get(String const& key, long& L) const
    {
        return Get(key, (int64_t&)L);
    }

    bool JsonObject::Get(String const& key, int32_t& L) const
    {
        json_object* obj = GetJsonObject(key);
        if (!obj) return false;

        switch (json_object_get_type(obj))
        {
        case json_type_int:
            {
                L = (int32_t)json_object_get_int(obj);
                return true;
            }
        /*
        case json_type_int64:
            {
                L = (int32_t)json_object_get_int64(obj);
                return true;
            }
        */
        case json_type_string:
            {
                const char* sz = json_object_get_string(obj);
                if (sz)
                {
                    L = (int32_t)strtol(sz, NULL, 10);
                    return true;
                }
                break;
            }
        default:
            break;
        }
        return false;
    }    

    bool JsonObject::Get(String const& key, uint32_t& L) const
    {
        json_object* obj = GetJsonObject(key);
        if (!obj) return false;

        switch (json_object_get_type(obj))
        {
        case json_type_int:
            {
                L = (uint32_t)json_object_get_int(obj);
                return true;
            }
        /*
        case json_type_int64:
            {
                L = (uint32_t)json_object_get_int64(obj);
                return true;
            }
        */
        case json_type_string:
            {
                const char* sz = json_object_get_string(obj);
                if (sz)
                {
                    L = (uint32_t)strtoul(sz, NULL, 10);
                    return true;
                }
                break;
            }
        default:
            break;
        }
        return false;
    }

    bool JsonObject::Get(String const& key, int64_t& val) const
    {
        json_object* obj = GetJsonObject(key);
        if (!obj) return false;

        switch (json_object_get_type(obj))
        {
        case json_type_int:
            {
                val = (int64_t)json_object_get_int(obj);
                return true;
            }
        /*
        case json_type_int64:
            {
                val = json_object_get_int64(obj);
                return true;
            }
        */
        case json_type_string:
            {
                const char* sz = json_object_get_string(obj);
                if (sz)
                {
                    val = (int64_t)strtoll(sz, NULL, 10);
                    return true;
                }
                break;
            }
        default:
            break;
        }
        return false;
    }

    /*
    bool JsonObject::Get(String const& key, uint64_t& val) const
    {
        json_object* obj = GetJsonObject(key);
        if (!obj) return false;

        switch (json_object_get_type(obj))
        {
        case json_type_int:
            {
                val = (uint64_t)json_object_get_int(obj);
                return true;
            }
        case json_type_int64:
            {
                val = (uint64_t)json_object_get_int64(obj);
                return true;
            }
        case json_type_string:
            {
                const char* sz = json_object_get_string(obj);
                if (sz)
                {
                    val = (int64_t)strtoull(sz, NULL, 10);
                    return true;
                }
                break;
            }
        default:
            break;
        }
        return false;
    }
    */

#ifdef __GNUC__
    bool JsonObject::Get(String const& key, size_t& val) const
    {
        json_object* obj = GetJsonObject(key);
        if (!obj) return false;

        switch (json_object_get_type(obj))
        {
        case json_type_int:
            {
                val = (size_t)json_object_get_int(obj);
                return true;
            }
        case json_type_int64:
            {
                val = (size_t)json_object_get_int64(obj);
                return true;
            }
        case json_type_string:
            {
                const char* sz = json_object_get_string(obj);
                if (sz)
                {
                    val = (size_t)strtoull(sz, NULL, 10);
                    return true;
                }
                break;
            }
        default:
            break;
        }
        return false;
    }
#endif
    /*
    bool JsonObject::Get(String const& key, int& i) const
    {
        json_object* obj = GetJsonObject(key);
        if (!obj) return false;

        switch (json_object_get_type(obj))
        {
            case json_type_int:
            {
                i = json_object_get_int(obj);
                return true;
            }
            case json_type_string:
            {
                const char* sz = json_object_get_string(obj);
                if (sz)
                {
                    i = atoi(sz);
                    return true;
                }
                break;
            }
            case json_type_int64:
            {
                i = (int)json_object_get_int64(obj);
                return true;
            }
            default:
            break;
        }
        return false;
        */
        /*
        json_object* obj = GetJsonObject(key);
        if (obj && json_object_is_type(obj, json_type_int))
        {
            i = json_object_get_int(obj);
            return true;
        }
        else
        {
            return false;
        }
    }
    */

    bool JsonObject::Get(String const& key, double& d) const
    {
        json_object* obj = GetJsonObject(key);
        if (obj && json_object_is_type(obj, json_type_double))
        {
            d = json_object_get_double(obj);
            return true;
        }
        else
        {
            return false;
        }
    }

    bool JsonObject::Get(String const& key, bool& b) const
    {
        json_object* obj = GetJsonObject(key);
        if (obj && json_object_is_type(obj, json_type_boolean))
        {
            b = (json_object_get_boolean(obj) != 0);
            return true;
        }
        else
        {
            return false;
        }
    }

    bool JsonObject::Get(String const& key, String& str) const
    {
        json_object* obj = GetJsonObject(key);
        if (obj && json_object_is_type(obj, json_type_string))
        {
            const char* sz = json_object_get_string(obj);
            if (sz)
            {
                str.assign(sz);
                return true;
            }
        }

        return false;
    }

    bool JsonObject::Get(String const& key, Json& json) const
    {
        json_object* obj = GetJsonObject(key);
        if (obj)
        {
            json = Json(json_object_get(obj));
            return true;
        }
        else
        {
            return false;
        }
    }


    Json JsonObject::operator[](String const& key)
    {
        Json tmp;
        if (!HasKey(key))
        {
            Set(key, "");
        }
        Get(key, tmp);
        return tmp;
    }

    Json JsonObject::operator[](String const& key) const
    {
        Json tmp;
        if (!Get(key, tmp))
        {
            throw KeyNotFound(key);
        }
        return tmp;
    }



    bool JsonObject::Get(String const& key, JsonObject& obj) const
    {
        json_object* tmp = GetJsonObject(key);
        if (tmp && json_object_is_type(tmp, json_type_object))
        {
            obj = JsonObject(Json(json_object_get(tmp)));
            return true;
        }
        else
        {
            return false;
        }
    }

    bool JsonObject::Get(String const& key, JsonArray& arr) const
    {
        json_object* tmp = GetJsonObject(key);
        if (tmp && json_object_is_type(tmp, json_type_array))
        {
            arr = JsonArray(Json(json_object_get(tmp)));
            return true;
        }
        else
        {
            return false;
        }
    }

    /*
    void JsonObject::Set(String const& key, int i)
    {
        Set(key, (int32_t)i);
    }*/

    void JsonObject::Set(String const& key, int32_t i)
    {
        QV_ASSERT(!key.empty());
        json_object *tmp = json_object_new_int(i);
        QV_ASSERT(tmp);

        json_object_object_add(m_json.m_json, key.c_str(), tmp);
    }

    void JsonObject::Set(String const& key, uint32_t u)
    {
        int32_t i = (int32_t)u;
        if (i < 0)
        {
            // set as uint64_t
            Set(key, (uint64_t)u);
        }
        else
        {
            Set(key, i);
        }
    }

    /*
    void JsonObject::Set(String const& key, int64_t i)
    {
        QV_ASSERT(!key.empty());
        json_object *tmp = json_object_new_int64(i);
        QV_ASSERT(tmp);

        json_object_object_add(m_json.m_json, key.c_str(), tmp);
    }

    void JsonObject::Set(String const& key, uint64_t u)
    {
        int64_t i = (int64_t)u;

        if (i < 0)
        {
            // set as String
            Set(key, boost::lexical_cast<String>(u));
        }
        else
        {
            // set as int64_t
            Set(key, i);
        }
    }
    */
#ifdef __GNUC__
    void JsonObject::Set(String const& key, size_t i)
    {
        if (i < INT32_MAX)
        {
            Set(key, (int32_t)i);
        }
        else 
        {
            Set(key, (int64_t)i);
        }
    }

    void JsonObject::Set(String const& key, time_t t)
    {
        Set(key, (int64_t)t);
    }
#endif
    void JsonObject::Set(String const& key, double d)
    {
        QV_ASSERT(!key.empty());
        json_object *tmp = json_object_new_double(d);
        QV_ASSERT(tmp);

        json_object_object_add(m_json.m_json, key.c_str(), tmp);
    }

    void JsonObject::Set(String const& key, bool b)
    {
        QV_ASSERT(!key.empty());
        json_object *tmp = json_object_new_boolean((boolean)b);
        QV_ASSERT(tmp);

        json_object_object_add(m_json.m_json, key.c_str(), tmp);
    }

    void JsonObject::Set(String const& key, const char* sz, int len)
    {
        QV_ASSERT(!key.empty());
        json_object *tmp = NULL;
        if (len > 0)
        {
            tmp = json_object_new_string_len(sz, len);
        }
        else
        {
            tmp = json_object_new_string(sz);
        }
        QV_ASSERT(tmp);

        json_object_object_add(m_json.m_json, key.c_str(), tmp);
    }


    void JsonObject::Set(String const& key, String const& str)
    {
        Set(key, str.c_str());
    }

    void JsonObject::Set(String const& key, Json const& json)
    {
        QV_ASSERT(!key.empty());
        json_object *tmp = json_object_get(json.m_json);
        QV_ASSERT(tmp);

        json_object_object_add(m_json.m_json, key.c_str(), tmp);
    }

    void JsonObject::Set(String const& key, JsonObject const& obj)
    {
        Set(key, obj.m_json);
    }

    void JsonObject::Set(String const& key, JsonArray const& arr)
    {
        Set(key, arr.ToJson());
    }

    Json const& JsonObject::GetJson() const
    {
        return m_json;
    }

    Json& JsonObject::GetJson() 
    {
        return m_json;
    }

    Json JsonObject::ToJson() const
    {
        return m_json;
    }

    String JsonObject::ToString() const
    {
        return m_json.ToString();
    }

    String JsonObject::ToPrettyString() const
    {
        ostringstream out;

        m_json.ToPrettyStringEx(out);

        out << endl;

        return out.str();
    }

    /*
    String JsonObject::ToQueryString() const
    {
        std::ostringstream strm;

        if (m_json.m_json)
        {
            int cnt = 0;
            json_object_object_foreach(m_json.m_json, _key, _val)
            {
                if (_key && _val)
                {
                    if (cnt++)
                    strm << "&";
                    // TODO: urlencode val
                    strm << _key
                    << "="
                    << json_object_get_string(_val);
                }
            }
        }
        return strm.str();
    }
    */    
    std::map<String, String> JsonObject::ToMap() const
    {
        std::map<String, String> ret;

        if (m_json.m_json)
        {
            json_object_object_foreach(m_json.m_json, _key, _val)
            {
                if (_key && _val)
                {
                    const char* sz = json_object_get_string(_val);
                    if (sz)
                    {
                        ret.insert(std::make_pair(_key, sz));
                    }
                }
            }
        }
        return ret;
    }

    void JsonObject::Merge(JsonObject const& obj)
    {
        Json json(obj.GetJson());

        json_object_object_foreach(json.m_json, _key, _val)
        {
            if (_key && _val)
            {
                Set(_key, Json(json_object_get(_val)));
            }
        }
    }

    bool JsonObject::Copy(JsonObject& to, String const& toKey, JsonObject const& from, String const& fromKey)
    {
        Json tmp;
        if (from.Get(fromKey, tmp))
        {
            to.Set(toKey, tmp);
            return true;
        }

        return false;
    }
}