#ifndef JASON_SPIRIT_VALUE
#define JASON_SPIRIT_VALUE

/* Copyright (c) 2007-2009 John W Wilkinson

   This source code can be used for any purpose as long as
   this comment is retained. */

// json spirit version 4.00

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

#include <vector>
#include <map>
#include <string>
#include <cassert>
#include <boost/config.hpp>
#include <boost/cstdint.hpp>
#include <boost/shared_ptr.hpp>

namespace json_spirit
{
enum Value_type
{
    obj_type,
    array_type,
    str_type,
    bool_type,
    int_type,
    real_type,
    null_type
};

template <class Config>  // Config determines whether the value uses std::string or std::wstring and
                         // whether JSON Objects are represented as vectors or maps
class Value_impl
{
public:
    typedef Config Config_type;
    typedef typename Config::String_type String_type;
    typedef typename Config::Object_type Object;
    typedef typename Config::Array_type Array;
    typedef typename String_type::const_pointer Const_str_ptr;  // eg const char*

    Value_impl();  // creates null value
    Value_impl(Const_str_ptr value);
    Value_impl(const String_type& value);
    Value_impl(const Object& value);
    Value_impl(const Array& value);
    Value_impl(bool value);
    Value_impl(int value);
    Value_impl(boost::int64_t value);
    Value_impl(boost::uint64_t value);
    Value_impl(double value);

    Value_impl(const Value_impl& other);

    bool operator==(const Value_impl& lhs) const;

    Value_impl& operator=(const Value_impl& lhs);

    Value_type type() const;

    bool is_uint64() const;
    bool is_null() const;

    const String_type& get_str() const;
    const Object& get_obj() const;
    const Array& get_array() const;
    bool get_bool() const;
    int get_int() const;
    boost::int64_t get_int64() const;
    boost::uint64_t get_uint64() const;
    double get_real() const;

    Object& get_obj();
    Array& get_array();

    template <typename T>
    T get_value() const;  // example usage: int    i = value.get_value< int >();
                          // or             double d = value.get_value< double >();

    static const Value_impl null;

private:
    Value_type type_;

    typedef boost::shared_ptr<Object> Object_ptr;
    typedef boost::shared_ptr<Array> Array_ptr;

    String_type str_;
    Object_ptr obj_p_;
    Array_ptr array_p_;
    union
    {
        bool bool_;
        boost::int64_t i_;
        double d_;
    };
    bool is_uint64_;
};

// vector objects

template <class Config>
struct Pair_impl
{
    typedef typename Config::String_type String_type;
    typedef typename Config::Value_type Value_type;

    Pair_impl(const String_type& name, const Value_type& value);

    bool operator==(const Pair_impl& lhs) const;

    String_type name_;
    Value_type value_;
};

template <class String>
struct Config_vector
{
    typedef String String_type;
    typedef Value_impl<Config_vector> Value_type;
    typedef Pair_impl<Config_vector> Pair_type;
    typedef std::vector<Value_type> Array_type;
    typedef std::vector<Pair_type> Object_type;

    static Value_type& add(Object_type& obj, const String_type& name, const Value_type& value)
    {
        obj.push_back(Pair_type(name, value));

        return obj.back().value_;
    }

    static String_type get_name(const Pair_type& pair)
    {
        return pair.name_;
    }

    static Value_type get_value(const Pair_type& pair)
    {
        return pair.value_;
    }
};

// typedefs for ASCII

typedef Config_vector<std::string> Config;

typedef Config::Value_type Value;
typedef Config::Pair_type Pair;
typedef Config::Object_type Object;
typedef Config::Array_type Array;

// typedefs for Unicode

#ifndef BOOST_NO_STD_WSTRING

typedef Config_vector<std::wstring> wConfig;

typedef wConfig::Value_type wValue;
typedef wConfig::Pair_type wPair;
typedef wConfig::Object_type wObject;
typedef wConfig::Array_type wArray;
#endif

// map objects

template <class String>
struct Config_map
{
    typedef String String_type;
    typedef Value_impl<Config_map> Value_type;
    typedef std::vector<Value_type> Array_type;
    typedef std::map<String_type, Value_type> Object_type;
    typedef typename Object_type::value_type Pair_type;

    static Value_type& add(Object_type& obj, const String_type& name, const Value_type& value)
    {
        return obj[name] = value;
    }

    static String_type get_name(const Pair_type& pair)
    {
        return pair.first;
    }

    static Value_type get_value(const Pair_type& pair)
    {
        return pair.second;
    }
};

// typedefs for ASCII

typedef Config_map<std::string> mConfig;

typedef mConfig::Value_type mValue;
typedef mConfig::Object_type mObject;
typedef mConfig::Array_type mArray;

// typedefs for Unicode

#ifndef BOOST_NO_STD_WSTRING

typedef Config_map<std::wstring> wmConfig;

typedef wmConfig::Value_type wmValue;
typedef wmConfig::Object_type wmObject;
typedef wmConfig::Array_type wmArray;

#endif

///////////////////////////////////////////////////////////////////////////////////////////////
//
// implementation

template <class Config>
const Value_impl<Config> Value_impl<Config>::null;

template <class Config>
Value_impl<Config>::Value_impl() :
    type_(null_type),
    is_uint64_(false)
{
}

template <class Config>
Value_impl<Config>::Value_impl(const Const_str_ptr value) :
    type_(str_type),
    str_(value),
    is_uint64_(false)
{
}

template <class Config>
Value_impl<Config>::Value_impl(const String_type& value) :
    type_(str_type),
    str_(value),
    is_uint64_(false)
{
}

template <class Config>
Value_impl<Config>::Value_impl(const Object& value) :
    type_(obj_type),
    obj_p_(new Object(value)),
    is_uint64_(false)
{
}

template <class Config>
Value_impl<Config>::Value_impl(const Array& value) :
    type_(array_type),
    array_p_(new Array(value)),
    is_uint64_(false)
{
}

template <class Config>
Value_impl<Config>::Value_impl(bool value) :
    type_(bool_type),
    bool_(value),
    is_uint64_(false)
{
}

template <class Config>
Value_impl<Config>::Value_impl(int value) :
    type_(int_type),
    i_(value),
    is_uint64_(false)
{
}

template <class Config>
Value_impl<Config>::Value_impl(boost::int64_t value) :
    type_(int_type),
    i_(value),
    is_uint64_(false)
{
}

template <class Config>
Value_impl<Config>::Value_impl(boost::uint64_t value) :
    type_(int_type),
    i_(static_cast<boost::int64_t>(value)),
    is_uint64_(true)
{
}

template <class Config>
Value_impl<Config>::Value_impl(double value) :
    type_(real_type),
    d_(value),
    is_uint64_(false)
{
}

template <class Config>
Value_impl<Config>::Value_impl(const Value_impl<Config>& other) :
    type_(other.type()),
    is_uint64_(other.is_uint64_)
{
    switch (type_) {
        case str_type:
            str_ = other.get_str();
            break;
        case obj_type:
            obj_p_ = Object_ptr(new Object(other.get_obj()));
            break;
        case array_type:
            array_p_ = Array_ptr(new Array(other.get_array()));
            break;
        case bool_type:
            bool_ = other.get_bool();
            break;
        case int_type:
            i_ = other.get_int64();
            break;
        case real_type:
            d_ = other.get_real();
            break;
        case null_type:
            break;
        default:
            assert(false);
    };
}

template <class Config>
Value_impl<Config>& Value_impl<Config>::operator=(const Value_impl& lhs)
{
    Value_impl tmp(lhs);

    std::swap(type_, tmp.type_);
    str_.swap(tmp.str_);
    obj_p_.swap(tmp.obj_p_);
    array_p_.swap(tmp.array_p_);
    std::swap(is_uint64_, tmp.is_uint64_);

    switch (type_) {
        case bool_type:
            std::swap(bool_, tmp.bool_);
            break;
        case int_type:
            std::swap(i_, tmp.i_);
            break;
        case real_type:
            std::swap(d_, tmp.d_);
            break;
        default:
            break;
    };

    return *this;
}

template <class Config>
bool Value_impl<Config>::operator==(const Value_impl& lhs) const
{
    if (this == &lhs)
        return true;

    if (type() != lhs.type())
        return false;

    switch (type_) {
        case str_type:
            return get_str() == lhs.get_str();
        case obj_type:
            return get_obj() == lhs.get_obj();
        case array_type:
            return get_array() == lhs.get_array();
        case bool_type:
            return get_bool() == lhs.get_bool();
        case int_type:
            return (get_int64() == lhs.get_int64()) &&
                   (is_uint64() == lhs.is_uint64());
        case real_type:
            return get_real() == lhs.get_real();
        case null_type:
            return true;
    };

    assert(false);

    return false;
}

template <class Config>
Value_type Value_impl<Config>::type() const
{
    return type_;
}

template <class Config>
bool Value_impl<Config>::is_uint64() const
{
    return is_uint64_;
}

template <class Config>
bool Value_impl<Config>::is_null() const
{
    return type() == null_type;
}

template <class Config>
const typename Config::String_type& Value_impl<Config>::get_str() const
{
    assert(type() == str_type);

    return str_;
}

template <class Config>
const typename Value_impl<Config>::Object& Value_impl<Config>::get_obj() const
{
    assert(type() == obj_type);

    return *obj_p_;
}

template <class Config>
const typename Value_impl<Config>::Array& Value_impl<Config>::get_array() const
{
    assert(type() == array_type);

    return *array_p_;
}

template <class Config>
bool Value_impl<Config>::get_bool() const
{
    assert(type() == bool_type);

    return bool_;
}

template <class Config>
int Value_impl<Config>::get_int() const
{
    assert(type() == int_type);

    return static_cast<int>(i_);
}

template <class Config>
boost::int64_t Value_impl<Config>::get_int64() const
{
    assert(type() == int_type);

    return i_;
}

template <class Config>
boost::uint64_t Value_impl<Config>::get_uint64() const
{
    assert(type() == int_type);

    return static_cast<boost::uint64_t>(i_);
}

template <class Config>
double Value_impl<Config>::get_real() const
{
    if (type() == int_type) {
        return is_uint64() ? static_cast<double>(get_uint64())
                           : static_cast<double>(get_int64());
    }

    assert(type() == real_type);

    return d_;
}

template <class Config>
typename Value_impl<Config>::Object& Value_impl<Config>::get_obj()
{
    assert(type() == obj_type);

    return *obj_p_;
}

template <class Config>
typename Value_impl<Config>::Array& Value_impl<Config>::get_array()
{
    assert(type() == array_type);

    return *array_p_;
}

template <class Config>
Pair_impl<Config>::Pair_impl(const String_type& name, const Value_type& value) :
    name_(name),
    value_(value)
{
}

template <class Config>
bool Pair_impl<Config>::operator==(const Pair_impl<Config>& lhs) const
{
    if (this == &lhs)
        return true;

    return (name_ == lhs.name_) && (value_ == lhs.value_);
}

// converts a C string, ie. 8 bit char array, to a string object
//
template <class String_type>
String_type to_str(const char* c_str)
{
    String_type result;

    for (const char* p = c_str; *p != 0; ++p) {
        result += *p;
    }

    return result;
}

//

namespace internal_
{
template <typename T>
struct Type_to_type
{
};

template <class Value>
int get_value(const Value& value, Type_to_type<int>)
{
    return value.get_int();
}

template <class Value>
boost::int64_t get_value(const Value& value, Type_to_type<boost::int64_t>)
{
    return value.get_int64();
}

template <class Value>
boost::uint64_t get_value(const Value& value, Type_to_type<boost::uint64_t>)
{
    return value.get_uint64();
}

template <class Value>
double get_value(const Value& value, Type_to_type<double>)
{
    return value.get_real();
}

template <class Value>
typename Value::String_type get_value(const Value& value, Type_to_type<typename Value::String_type>)
{
    return value.get_str();
}

template <class Value>
typename Value::Array get_value(const Value& value, Type_to_type<typename Value::Array>)
{
    return value.get_array();
}

template <class Value>
typename Value::Object get_value(const Value& value, Type_to_type<typename Value::Object>)
{
    return value.get_obj();
}

template <class Value>
bool get_value(const Value& value, Type_to_type<bool>)
{
    return value.get_bool();
}
}  // namespace internal_

template <class Config>
template <typename T>
T Value_impl<Config>::get_value() const
{
    return internal_::get_value(*this, internal_::Type_to_type<T>());
}
}  // namespace json_spirit

#endif
