#include <xrpl/beast/core/LexicalCast.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/detail/json_assert.h>
#include <xrpl/json/json_forwards.h>
#include <xrpl/json/json_value.h>
#include <xrpl/json/json_writer.h>

#include <cmath>
#include <cstdlib>
#include <cstring>
#include <string>
#include <utility>

namespace Json {

Value const Value::null;

class DefaultValueAllocator : public ValueAllocator
{
public:
    virtual ~DefaultValueAllocator() = default;

    char*
    makeMemberName(char const* memberName) override
    {
        return duplicateStringValue(memberName);
    }

    void
    releaseMemberName(char* memberName) override
    {
        releaseStringValue(memberName);
    }

    char*
    duplicateStringValue(char const* value, unsigned int length = unknown) override
    {
        //@todo investigate this old optimization
        // if ( !value  ||  value[0] == 0 )
        //   return 0;

        if (length == unknown)
            length = (value != nullptr) ? (unsigned int)strlen(value) : 0;

        char* newString = static_cast<char*>(malloc(length + 1));
        if (value != nullptr)
            memcpy(newString, value, length);
        newString[length] = 0;
        return newString;
    }

    void
    releaseStringValue(char* value) override
    {
        if (value != nullptr)
            free(value);
    }
};

static ValueAllocator*&
valueAllocator()
{
    static ValueAllocator* valueAllocator = new DefaultValueAllocator;
    return valueAllocator;
}

static struct DummyValueAllocatorInitializer
{
    DummyValueAllocatorInitializer()
    {
        valueAllocator();  // ensure valueAllocator() statics are initialized
                           // before main().
    }
} dummyValueAllocatorInitializer;

// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class Value::CZString
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////

// Notes: index_ indicates if the string was allocated when
// a string is stored.

Value::CZString::CZString(int index) : cstr_(0), index_(index)
{
}

Value::CZString::CZString(char const* cstr, DuplicationPolicy allocate)
    : cstr_(allocate == duplicate ? valueAllocator()->makeMemberName(cstr) : cstr), index_(allocate)
{
}

Value::CZString::CZString(CZString const& other)
    : cstr_(
          other.index_ != noDuplication && other.cstr_ != 0
              ? valueAllocator()->makeMemberName(other.cstr_)
              : other.cstr_)
    , index_([&]() -> int {
        if (!other.cstr_)
            return other.index_;
        return other.index_ == noDuplication ? noDuplication : duplicate;
    }())
{
}

Value::CZString::~CZString()
{
    if ((cstr_ != nullptr) && index_ == duplicate)
        valueAllocator()->releaseMemberName(const_cast<char*>(cstr_));
}

bool
Value::CZString::operator<(CZString const& other) const
{
    if ((cstr_ != nullptr) && (other.cstr_ != nullptr))
        return strcmp(cstr_, other.cstr_) < 0;

    return index_ < other.index_;
}

bool
Value::CZString::operator==(CZString const& other) const
{
    if ((cstr_ != nullptr) && (other.cstr_ != nullptr))
        return strcmp(cstr_, other.cstr_) == 0;

    return index_ == other.index_;
}

int
Value::CZString::index() const
{
    return index_;
}

char const*
Value::CZString::c_str() const
{
    return cstr_;
}

bool
Value::CZString::isStaticString() const
{
    return index_ == noDuplication;
}

// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class Value::Value
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////

/*! \internal Default constructor initialization must be equivalent to:
 * memset( this, 0, sizeof(Value) )
 * This optimization is used in ValueInternalMap fast allocator.
 */
Value::Value(ValueType type) : type_(type)
{
    switch (type)
    {
        case nullValue:
            break;

        case intValue:
        case uintValue:
            value_.int_ = 0;
            break;

        case realValue:
            value_.real_ = 0.0;
            break;

        case stringValue:
            value_.string_ = 0;
            break;

        case arrayValue:
        case objectValue:
            value_.map_ = new ObjectValues();
            break;

        case booleanValue:
            value_.bool_ = false;
            break;

        // LCOV_EXCL_START
        default:
            UNREACHABLE("Json::Value::Value(ValueType) : invalid type");
            // LCOV_EXCL_STOP
    }
}

Value::Value(Int value) : type_(intValue)
{
    value_.int_ = value;
}

Value::Value(UInt value) : type_(uintValue)
{
    value_.uint_ = value;
}

Value::Value(double value) : type_(realValue)
{
    value_.real_ = value;
}

Value::Value(char const* value) : type_(stringValue), allocated_(true)
{
    value_.string_ = valueAllocator()->duplicateStringValue(value);
}

Value::Value(xrpl::Number const& value) : type_(stringValue), allocated_(true)
{
    auto const tmp = to_string(value);
    value_.string_ = valueAllocator()->duplicateStringValue(tmp.c_str(), tmp.length());
}

Value::Value(std::string const& value) : type_(stringValue), allocated_(true)
{
    value_.string_ =
        valueAllocator()->duplicateStringValue(value.c_str(), (unsigned int)value.length());
}

Value::Value(StaticString const& value) : type_(stringValue)
{
    value_.string_ = const_cast<char*>(value.c_str());
}

Value::Value(bool value) : type_(booleanValue)
{
    value_.bool_ = value;
}

Value::Value(Value const& other) : type_(other.type_)
{
    switch (type_)
    {
        case nullValue:
        case intValue:
        case uintValue:
        case realValue:
        case booleanValue:
            value_ = other.value_;
            break;

        case stringValue:
            if (other.value_.string_ != nullptr)
            {
                value_.string_ = valueAllocator()->duplicateStringValue(other.value_.string_);
                allocated_ = true;
            }
            else
            {
                value_.string_ = 0;
            }

            break;

        case arrayValue:
        case objectValue:
            value_.map_ = new ObjectValues(*other.value_.map_);
            break;

        // LCOV_EXCL_START
        default:
            UNREACHABLE("Json::Value::Value(Value const&) : invalid type");
            // LCOV_EXCL_STOP
    }
}

Value::~Value()
{
    switch (type_)
    {
        case nullValue:
        case intValue:
        case uintValue:
        case realValue:
        case booleanValue:
            break;

        case stringValue:
            if (allocated_)
                valueAllocator()->releaseStringValue(value_.string_);

            break;

        case arrayValue:
        case objectValue:
            if (value_.map_ != nullptr)
                delete value_.map_;
            break;

        // LCOV_EXCL_START
        default:
            UNREACHABLE("Json::Value::~Value : invalid type");
            // LCOV_EXCL_STOP
    }
}

Value&
Value::operator=(Value const& other)
{
    Value tmp(other);
    swap(tmp);
    return *this;
}

Value::Value(Value&& other) noexcept
    : value_(other.value_), type_(other.type_), allocated_(other.allocated_)
{
    other.type_ = nullValue;
    other.allocated_ = 0;
}

Value&
Value::operator=(Value&& other)
{
    Value tmp(std::move(other));
    swap(tmp);
    return *this;
}

void
Value::swap(Value& other) noexcept
{
    std::swap(value_, other.value_);

    ValueType temp = type_;
    type_ = other.type_;
    other.type_ = temp;

    int temp2 = allocated_;
    allocated_ = other.allocated_;
    other.allocated_ = temp2;
}

ValueType
Value::type() const
{
    return type_;
}

static int
integerCmp(Int i, UInt ui)
{
    // All negative numbers are less than all unsigned numbers.
    if (i < 0)
        return -1;

    // Now we can safely compare.
    if (i < ui)
        return -1;
    return (i == ui) ? 0 : 1;
}

bool
operator<(Value const& x, Value const& y)
{
    if (auto signum = x.type_ - y.type_)
    {
        if (x.type_ == intValue && y.type_ == uintValue)
        {
            signum = integerCmp(x.value_.int_, y.value_.uint_);
        }
        else if (x.type_ == uintValue && y.type_ == intValue)
        {
            signum = -integerCmp(y.value_.int_, x.value_.uint_);
        }
        return signum < 0;
    }

    switch (x.type_)
    {
        case nullValue:
            return false;

        case intValue:
            return x.value_.int_ < y.value_.int_;

        case uintValue:
            return x.value_.uint_ < y.value_.uint_;

        case realValue:
            return x.value_.real_ < y.value_.real_;

        case booleanValue:
            return static_cast<int>(x.value_.bool_) < static_cast<int>(y.value_.bool_);

        case stringValue:
            return (x.value_.string_ == 0 && (y.value_.string_ != nullptr)) ||
                ((y.value_.string_ != nullptr) && (x.value_.string_ != nullptr) &&
                 strcmp(x.value_.string_, y.value_.string_) < 0);

        case arrayValue:
        case objectValue: {
            if (int signum = int(x.value_.map_->size()) - y.value_.map_->size())
                return signum < 0;

            return *x.value_.map_ < *y.value_.map_;
        }

            // LCOV_EXCL_START
        default:
            UNREACHABLE("Json::operator<(Value, Value) : invalid type");
            // LCOV_EXCL_STOP
    }

    return false;  // unreachable
}

bool
operator==(Value const& x, Value const& y)
{
    if (x.type_ != y.type_)
    {
        if (x.type_ == intValue && y.type_ == uintValue)
            return integerCmp(x.value_.int_, y.value_.uint_) == 0;
        if (x.type_ == uintValue && y.type_ == intValue)
            return integerCmp(y.value_.int_, x.value_.uint_) == 0;
        return false;
    }

    switch (x.type_)
    {
        case nullValue:
            return true;

        case intValue:
            return x.value_.int_ == y.value_.int_;

        case uintValue:
            return x.value_.uint_ == y.value_.uint_;

        case realValue:
            return x.value_.real_ == y.value_.real_;

        case booleanValue:
            return x.value_.bool_ == y.value_.bool_;

        case stringValue:
            return x.value_.string_ == y.value_.string_ ||
                ((y.value_.string_ != nullptr) && (x.value_.string_ != nullptr) &&
                 (strcmp(x.value_.string_, y.value_.string_) == 0));

        case arrayValue:
        case objectValue:
            return x.value_.map_->size() == y.value_.map_->size() &&
                *x.value_.map_ == *y.value_.map_;

        // LCOV_EXCL_START
        default:
            UNREACHABLE("Json::operator==(Value, Value) : invalid type");
            // LCOV_EXCL_STOP
    }

    return false;  // unreachable
}

char const*
Value::asCString() const
{
    XRPL_ASSERT(type_ == stringValue, "Json::Value::asCString : valid type");
    return value_.string_;
}

std::string
Value::asString() const
{
    switch (type_)
    {
        case nullValue:
            return "";

        case stringValue:
            return (value_.string_ != nullptr) ? value_.string_ : "";

        case booleanValue:
            return value_.bool_ ? "true" : "false";

        case intValue:
            return std::to_string(value_.int_);

        case uintValue:
            return std::to_string(value_.uint_);

        case realValue:
            return std::to_string(value_.real_);

        case arrayValue:
        case objectValue:
            JSON_ASSERT_MESSAGE(false, "Type is not convertible to string");

            // LCOV_EXCL_START
        default:
            UNREACHABLE("Json::Value::asString : invalid type");
            // LCOV_EXCL_STOP
    }

    return "";  // unreachable
}

Value::Int
Value::asInt() const
{
    switch (type_)
    {
        case nullValue:
            return 0;

        case intValue:
            return value_.int_;

        case uintValue:
            JSON_ASSERT_MESSAGE(
                value_.uint_ < (unsigned)maxInt, "integer out of signed integer range");
            return value_.uint_;

        case realValue:
            JSON_ASSERT_MESSAGE(
                (value_.real_ >= minInt && value_.real_ <= maxInt),
                "Real out of signed integer range");
            return Int(value_.real_);

        case booleanValue:
            return value_.bool_ ? 1 : 0;

        case stringValue: {
            char const* const str{(value_.string_ != nullptr) ? value_.string_ : ""};
            return beast::lexicalCastThrow<int>(str);
        }

        case arrayValue:
        case objectValue:
            JSON_ASSERT_MESSAGE(false, "Type is not convertible to int");

            // LCOV_EXCL_START
        default:
            UNREACHABLE("Json::Value::asInt : invalid type");
            // LCOV_EXCL_STOP
    }

    return 0;  // unreachable;
}

UInt
Value::asAbsUInt() const
{
    switch (type_)
    {
        case nullValue:
            return 0;

        case intValue: {
            // Doing this conversion through int64 avoids overflow error for
            // value_.int_ = -1 * 2^31 i.e. numeric_limits<int>::min().
            if (value_.int_ < 0)
                return static_cast<std::int64_t>(value_.int_) * -1;
            return value_.int_;
        }

        case uintValue:
            return value_.uint_;

        case realValue: {
            if (value_.real_ < 0)
            {
                JSON_ASSERT_MESSAGE(
                    -1 * value_.real_ <= maxUInt, "Real out of unsigned integer range");
                return UInt(-1 * value_.real_);
            }
            JSON_ASSERT_MESSAGE(value_.real_ <= maxUInt, "Real out of unsigned integer range");
            return UInt(value_.real_);
        }

        case booleanValue:
            return value_.bool_ ? 1 : 0;

        case stringValue: {
            char const* const str{(value_.string_ != nullptr) ? value_.string_ : ""};
            auto const temp = beast::lexicalCastThrow<std::int64_t>(str);
            if (temp < 0)
            {
                JSON_ASSERT_MESSAGE(-1 * temp <= maxUInt, "String out of unsigned integer range");
                return -1 * temp;
            }
            JSON_ASSERT_MESSAGE(temp <= maxUInt, "String out of unsigned integer range");
            return temp;
        }

        case arrayValue:
        case objectValue:
            JSON_ASSERT_MESSAGE(false, "Type is not convertible to int");

            // LCOV_EXCL_START
        default:
            UNREACHABLE("Json::Value::asAbsInt : invalid type");
            // LCOV_EXCL_STOP
    }

    return 0;  // unreachable;
}

Value::UInt
Value::asUInt() const
{
    switch (type_)
    {
        case nullValue:
            return 0;

        case intValue:
            JSON_ASSERT_MESSAGE(
                value_.int_ >= 0, "Negative integer can not be converted to unsigned integer");
            return value_.int_;

        case uintValue:
            return value_.uint_;

        case realValue:
            JSON_ASSERT_MESSAGE(
                (value_.real_ >= 0 && value_.real_ <= maxUInt),
                "Real out of unsigned integer range");
            return UInt(value_.real_);

        case booleanValue:
            return value_.bool_ ? 1 : 0;

        case stringValue: {
            char const* const str{(value_.string_ != nullptr) ? value_.string_ : ""};
            return beast::lexicalCastThrow<unsigned int>(str);
        }

        case arrayValue:
        case objectValue:
            JSON_ASSERT_MESSAGE(false, "Type is not convertible to uint");

            // LCOV_EXCL_START
        default:
            UNREACHABLE("Json::Value::asUInt : invalid type");
            // LCOV_EXCL_STOP
    }

    return 0;  // unreachable;
}

double
Value::asDouble() const
{
    switch (type_)
    {
        case nullValue:
            return 0.0;

        case intValue:
            return value_.int_;

        case uintValue:
            return value_.uint_;

        case realValue:
            return value_.real_;

        case booleanValue:
            return value_.bool_ ? 1.0 : 0.0;

        case stringValue:
        case arrayValue:
        case objectValue:
            JSON_ASSERT_MESSAGE(false, "Type is not convertible to double");

            // LCOV_EXCL_START
        default:
            UNREACHABLE("Json::Value::asDouble : invalid type");
            // LCOV_EXCL_STOP
    }

    return 0;  // unreachable;
}

bool
Value::asBool() const
{
    switch (type_)
    {
        case nullValue:
            return false;

        case intValue:
        case uintValue:
            return value_.int_ != 0;

        case realValue:
            return value_.real_ != 0.0;

        case booleanValue:
            return value_.bool_;

        case stringValue:
            return (value_.string_ != nullptr) && value_.string_[0] != 0;

        case arrayValue:
        case objectValue:
            return !value_.map_->empty();

            // LCOV_EXCL_START
        default:
            UNREACHABLE("Json::Value::asBool : invalid type");
            // LCOV_EXCL_STOP
    }

    return false;  // unreachable;
}

bool
Value::isConvertibleTo(ValueType other) const
{
    switch (type_)
    {
        case nullValue:
            return true;

        case intValue:
            return (other == nullValue && value_.int_ == 0) || other == intValue ||
                (other == uintValue && value_.int_ >= 0) || other == realValue ||
                other == stringValue || other == booleanValue;

        case uintValue:
            return (other == nullValue && value_.uint_ == 0) ||
                (other == intValue && value_.uint_ <= (unsigned)maxInt) || other == uintValue ||
                other == realValue || other == stringValue || other == booleanValue;

        case realValue:
            return (other == nullValue && value_.real_ == 0.0) ||
                (other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt) ||
                (other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt &&
                 std::fabs(round(value_.real_) - value_.real_) <
                     std::numeric_limits<double>::epsilon()) ||
                other == realValue || other == stringValue || other == booleanValue;

        case booleanValue:
            return (other == nullValue && !value_.bool_) || other == intValue ||
                other == uintValue || other == realValue || other == stringValue ||
                other == booleanValue;

        case stringValue:
            return other == stringValue ||
                (other == nullValue && ((value_.string_ == nullptr) || value_.string_[0] == 0));

        case arrayValue:
            return other == arrayValue || (other == nullValue && value_.map_->empty());

        case objectValue:
            return other == objectValue || (other == nullValue && value_.map_->empty());

        // LCOV_EXCL_START
        default:
            UNREACHABLE("Json::Value::isConvertible : invalid type");
            // LCOV_EXCL_STOP
    }

    return false;  // unreachable;
}

/// Number of values in array or object
Value::UInt
Value::size() const
{
    switch (type_)
    {
        case nullValue:
        case intValue:
        case uintValue:
        case realValue:
        case booleanValue:
        case stringValue:
            return 0;

        case arrayValue:  // size of the array is highest index + 1
            if (!value_.map_->empty())
            {
                ObjectValues::const_iterator itLast = value_.map_->end();
                --itLast;
                return (*itLast).first.index() + 1;
            }

            return 0;

        case objectValue:
            return Int(value_.map_->size());

            // LCOV_EXCL_START
        default:
            UNREACHABLE("Json::Value::size : invalid type");
            // LCOV_EXCL_STOP
    }

    return 0;  // unreachable;
}

Value::
operator bool() const
{
    if (isNull())
        return false;

    if (isString())
    {
        auto s = asCString();
        return (s != nullptr) && (s[0] != 0);
    }

    return !(isArray() || isObject()) || (size() != 0u);
}

void
Value::clear()
{
    XRPL_ASSERT(
        type_ == nullValue || type_ == arrayValue || type_ == objectValue,
        "Json::Value::clear : valid type");

    switch (type_)
    {
        case arrayValue:
        case objectValue:
            value_.map_->clear();
            break;

        default:
            break;
    }
}

Value&
Value::operator[](UInt index)
{
    XRPL_ASSERT(
        type_ == nullValue || type_ == arrayValue, "Json::Value::operator[](UInt) : valid type");

    if (type_ == nullValue)
        *this = Value(arrayValue);

    CZString key(index);
    ObjectValues::iterator it = value_.map_->lower_bound(key);

    if (it != value_.map_->end() && (*it).first == key)
        return (*it).second;

    ObjectValues::value_type defaultValue(key, null);
    it = value_.map_->insert(it, defaultValue);
    return (*it).second;
}

Value const&
Value::operator[](UInt index) const
{
    XRPL_ASSERT(
        type_ == nullValue || type_ == arrayValue,
        "Json::Value::operator[](UInt) const : valid type");

    if (type_ == nullValue)
        return null;

    CZString key(index);
    ObjectValues::const_iterator it = value_.map_->find(key);

    if (it == value_.map_->end())
        return null;

    return (*it).second;
}

Value&
Value::operator[](char const* key)
{
    return resolveReference(key, false);
}

Value&
Value::resolveReference(char const* key, bool isStatic)
{
    XRPL_ASSERT(
        type_ == nullValue || type_ == objectValue, "Json::Value::resolveReference : valid type");

    if (type_ == nullValue)
        *this = Value(objectValue);

    CZString actualKey(key, isStatic ? CZString::noDuplication : CZString::duplicateOnCopy);
    ObjectValues::iterator it = value_.map_->lower_bound(actualKey);

    if (it != value_.map_->end() && (*it).first == actualKey)
        return (*it).second;

    ObjectValues::value_type defaultValue(actualKey, null);
    it = value_.map_->insert(it, defaultValue);
    Value& value = (*it).second;
    return value;
}

Value
Value::get(UInt index, Value const& defaultValue) const
{
    Value const* value = &((*this)[index]);
    return value == &null ? defaultValue : *value;
}

bool
Value::isValidIndex(UInt index) const
{
    return index < size();
}

Value const&
Value::operator[](char const* key) const
{
    XRPL_ASSERT(
        type_ == nullValue || type_ == objectValue,
        "Json::Value::operator[](const char*) const : valid type");

    if (type_ == nullValue)
        return null;

    CZString actualKey(key, CZString::noDuplication);
    ObjectValues::const_iterator it = value_.map_->find(actualKey);

    if (it == value_.map_->end())
        return null;

    return (*it).second;
}

Value&
Value::operator[](std::string const& key)
{
    return (*this)[key.c_str()];
}

Value const&
Value::operator[](std::string const& key) const
{
    return (*this)[key.c_str()];
}

Value&
Value::operator[](StaticString const& key)
{
    return resolveReference(key, true);
}

Value const&
Value::operator[](StaticString const& key) const
{
    return (*this)[key.c_str()];
}

Value&
Value::append(Value const& value)
{
    return (*this)[size()] = value;
}

Value&
Value::append(Value&& value)
{
    return (*this)[size()] = std::move(value);
}

Value
Value::get(char const* key, Value const& defaultValue) const
{
    Value const* value = &((*this)[key]);
    return value == &null ? defaultValue : *value;
}

Value
Value::get(std::string const& key, Value const& defaultValue) const
{
    return get(key.c_str(), defaultValue);
}

Value
Value::removeMember(char const* key)
{
    XRPL_ASSERT(
        type_ == nullValue || type_ == objectValue, "Json::Value::removeMember : valid type");

    if (type_ == nullValue)
        return null;

    CZString actualKey(key, CZString::noDuplication);
    ObjectValues::iterator it = value_.map_->find(actualKey);

    if (it == value_.map_->end())
        return null;

    Value old(it->second);
    value_.map_->erase(it);
    return old;
}

Value
Value::removeMember(std::string const& key)
{
    return removeMember(key.c_str());
}

bool
Value::isMember(char const* key) const
{
    if (type_ != objectValue)
        return false;

    Value const* value = &((*this)[key]);
    return value != &null;
}

bool
Value::isMember(std::string const& key) const
{
    return isMember(key.c_str());
}

bool
Value::isMember(StaticString const& key) const
{
    return isMember(key.c_str());
}

Value::Members
Value::getMemberNames() const
{
    XRPL_ASSERT(
        type_ == nullValue || type_ == objectValue, "Json::Value::getMemberNames : valid type");

    if (type_ == nullValue)
        return Value::Members();

    Members members;
    members.reserve(value_.map_->size());
    ObjectValues::const_iterator it = value_.map_->begin();
    ObjectValues::const_iterator itEnd = value_.map_->end();

    for (; it != itEnd; ++it)
        members.push_back(std::string((*it).first.c_str()));

    return members;
}

bool
Value::isNull() const
{
    return type_ == nullValue;
}

bool
Value::isBool() const
{
    return type_ == booleanValue;
}

bool
Value::isInt() const
{
    return type_ == intValue;
}

bool
Value::isUInt() const
{
    return type_ == uintValue;
}

bool
Value::isIntegral() const
{
    return type_ == intValue || type_ == uintValue || type_ == booleanValue;
}

bool
Value::isDouble() const
{
    return type_ == realValue;
}

bool
Value::isNumeric() const
{
    return isIntegral() || isDouble();
}

bool
Value::isString() const
{
    return type_ == stringValue;
}

bool
Value::isArray() const
{
    return type_ == arrayValue;
}

bool
Value::isArrayOrNull() const
{
    return type_ == nullValue || type_ == arrayValue;
}

bool
Value::isObject() const
{
    return type_ == objectValue;
}

bool
Value::isObjectOrNull() const
{
    return type_ == nullValue || type_ == objectValue;
}

std::string
Value::toStyledString() const
{
    StyledWriter writer;
    return writer.write(*this);
}

Value::const_iterator
Value::begin() const
{
    switch (type_)
    {
        case arrayValue:
        case objectValue:
            if (value_.map_ != nullptr)
                return const_iterator(value_.map_->begin());

            break;
        default:
            break;
    }

    return const_iterator();
}

Value::const_iterator
Value::end() const
{
    switch (type_)
    {
        case arrayValue:
        case objectValue:
            if (value_.map_ != nullptr)
                return const_iterator(value_.map_->end());

            break;
        default:
            break;
    }

    return const_iterator();
}

Value::iterator
Value::begin()
{
    switch (type_)
    {
        case arrayValue:
        case objectValue:
            if (value_.map_ != nullptr)
                return iterator(value_.map_->begin());
            break;
        default:
            break;
    }

    return iterator();
}

Value::iterator
Value::end()
{
    switch (type_)
    {
        case arrayValue:
        case objectValue:
            if (value_.map_ != nullptr)
                return iterator(value_.map_->end());
            break;
        default:
            break;
    }

    return iterator();
}

}  // namespace Json
