//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// 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)
//
// Official repository: https://github.com/boostorg/json
//

#ifndef BOOST_JSON_DETAIL_VALUE_HPP
#define BOOST_JSON_DETAIL_VALUE_HPP

#include <boost/json/fwd.hpp>
#include <boost/json/kind.hpp>
#include <boost/json/storage_ptr.hpp>
#include <cstdint>
#include <limits>
#include <new>
#include <utility>

namespace boost {
namespace json {
namespace detail {

struct key_t
{
};

#if 0
template<class T>
struct to_number_limit
    : std::numeric_limits<T>
{
};

template<class T>
struct to_number_limit<T const>
    : to_number_limit<T>
{
};

template<>
struct to_number_limit<long long>
{
    static constexpr long long (min)() noexcept
    {
        return -9223372036854774784;
    }

    static constexpr long long (max)() noexcept
    {
        return 9223372036854774784;
    }
};

template<>
struct to_number_limit<unsigned long long>
{
    static constexpr
    unsigned long long (min)() noexcept
    {
        return 0;
    }

    static constexpr
    unsigned long long (max)() noexcept
    {
        return 18446744073709549568ULL;
    }
};
#else

template<class T>
class to_number_limit
{
    // unsigned

    static constexpr
    double min1(std::false_type)
    {
        return 0.0;
    }

    static constexpr
    double max1(std::false_type)
    {
        return max2u(std::integral_constant<
            bool, (std::numeric_limits<T>::max)() ==
            UINT64_MAX>{});
    }

    static constexpr
    double max2u(std::false_type)
    {
        return static_cast<double>(
            (std::numeric_limits<T>::max)());
    }

    static constexpr
    double max2u(std::true_type)
    {
        return 18446744073709549568.0;
    }

    // signed

    static constexpr
    double min1(std::true_type)
    {
        return min2s(std::integral_constant<
            bool, (std::numeric_limits<T>::max)() ==
            INT64_MAX>{});
    }

    static constexpr
    double min2s(std::false_type)
    {
        return static_cast<double>(
            (std::numeric_limits<T>::min)());
    }

    static constexpr
    double min2s(std::true_type)
    {
        return -9223372036854774784.0;
    }

    static constexpr
    double max1(std::true_type)
    {
        return max2s(std::integral_constant<
            bool, (std::numeric_limits<T>::max)() ==
            INT64_MAX>{});
    }

    static constexpr
    double max2s(std::false_type)
    {
        return static_cast<double>(
            (std::numeric_limits<T>::max)());
    }

    static constexpr
    double max2s(std::true_type)
    {
        return 9223372036854774784.0;
    }

public:
    static constexpr
    double (min)() noexcept
    {
        return min1(std::is_signed<T>{});
    }

    static constexpr
    double (max)() noexcept
    {
        return max1(std::is_signed<T>{});
    }
};

#endif

struct scalar
{
    storage_ptr sp; // must come first
    kind k;         // must come second
    union
    {
        bool b;
        std::int64_t i;
        std::uint64_t u;
        double d;
    };

    explicit
    scalar(storage_ptr sp_ = {}) noexcept
        : sp(std::move(sp_))
        , k(json::kind::null)
    {
    }

    explicit
    scalar(bool b_,
        storage_ptr sp_ = {}) noexcept
        : sp(std::move(sp_))
        , k(json::kind::bool_)
        , b(b_)
    {
    }

    explicit
    scalar(std::int64_t i_,
        storage_ptr sp_ = {}) noexcept
        : sp(std::move(sp_))
        , k(json::kind::int64)
        , i(i_)
    {
    }

    explicit
    scalar(std::uint64_t u_,
        storage_ptr sp_ = {}) noexcept
        : sp(std::move(sp_))
        , k(json::kind::uint64)
        , u(u_)
    {
    }

    explicit
    scalar(double d_,
        storage_ptr sp_ = {}) noexcept
        : sp(std::move(sp_))
        , k(json::kind::double_)
        , d(d_)
    {
    }
};

struct access
{
    template<class Value, class... Args>
    static
    Value&
    construct_value(Value* p, Args&&... args)
    {
        return *reinterpret_cast<
            Value*>(::new(p) Value(
            std::forward<Args>(args)...));
    }

    template<class KeyValuePair, class... Args>
    static
    KeyValuePair&
    construct_key_value_pair(
        KeyValuePair* p, Args&&... args)
    {
        return *reinterpret_cast<
            KeyValuePair*>(::new(p)
                KeyValuePair(
                    std::forward<Args>(args)...));
    }

    template<class Value>
    static
    char const*
    release_key(
        Value& jv,
        std::size_t& len) noexcept
    {
        BOOST_ASSERT(jv.is_string());
        jv.str_.sp_.~storage_ptr();
        return jv.str_.impl_.release_key(len);
    }

    using index_t = std::uint32_t;

    template<class KeyValuePair>
    static
    index_t&
    next(KeyValuePair& e) noexcept
    {
        return e.next_;
    }

    template<class KeyValuePair>
    static
    index_t const&
    next(KeyValuePair const& e) noexcept
    {
        return e.next_;
    }
};

BOOST_JSON_DECL
std::size_t
hash_value_impl( value const& jv ) noexcept;

} // detail
} // namespace json
} // namespace boost

#endif
