//
// 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
//

#include <boost/json/monotonic_resource.hpp>
#include <boost/json/parse.hpp>
#include <boost/json/static_resource.hpp>
#include <boost/json/storage_ptr.hpp>
#include <boost/json/value.hpp>

#include <iostream>

#include "test_suite.hpp"

namespace boost {
namespace json {

//----------------------------------------------------------

static void set1() {

//----------------------------------------------------------
{
//[doc_storage_ptr_1
storage_ptr sp1;
storage_ptr sp2;

assert( sp1.get() != nullptr );                         // always points to a valid resource
assert( sp1.get() == sp2.get() );                       // both point to the default resource
assert( *sp1.get() == *sp2.get() );                     // the default resource compares equal
//]
}
//----------------------------------------------------------
{
//[doc_storage_ptr_2
array arr;                                              // default construction
object obj;
string str;
value jv;

assert( jv.storage().get() == storage_ptr().get() );    // uses the default memory resource
assert( jv.storage().get() == arr.storage().get() );    // both point to the default resource
assert( *arr.storage() == *obj.storage() );             // containers use equivalent resources
//]
}
//----------------------------------------------------------
{
//[doc_storage_ptr_3
monotonic_resource mr;

value const jv = parse( "[1,2,3]", &mr );
//]
}
//----------------------------------------------------------

} // set1

//----------------------------------------------------------

//[doc_storage_ptr_4
value parse_value( string_view s)
{
    return parse( s, make_shared_resource< monotonic_resource >() );
}
//]

//----------------------------------------------------------

//[doc_storage_ptr_5
template< class Handler >
void do_rpc( string_view s, Handler&& h )
{
    unsigned char buffer[ 8192 ];                       // Small stack buffer to avoid most allocations during parse
    monotonic_resource mr( buffer );                    // This resource will use our local buffer first
    value const jv = parse( s, &mr );                   // Parse the input string into a value that uses our resource
    h( jv );                                            // Call the handler to perform the RPC command
}
//]

//----------------------------------------------------------

void set2() {

//----------------------------------------------------------
{
//[doc_storage_ptr_6
unsigned char buffer[ 8192 ];
static_resource mr( buffer );                           // The resource will use our local buffer
//]
}
//----------------------------------------------------------
{
//[doc_storage_ptr_7
monotonic_resource mr;
array arr( &mr );                                       // construct an array using our resource
arr.emplace_back( "boost" );                            // insert a string
assert( *arr[0].as_string().storage() == mr );          // the resource is propagated to the string
//]
}
//----------------------------------------------------------
{
//[doc_storage_ptr_8
{
    monotonic_resource mr;

    array arr( &mr );                                   // construct an array using our resource

    assert( ! arr.storage().is_shared() );              // no shared ownership
}
//]
}
//----------------------------------------------------------
{
//[doc_storage_ptr_9
storage_ptr sp = make_shared_resource< monotonic_resource >();

string str( sp );

assert( sp.is_shared() );                               // shared ownership
assert( str.storage().is_shared() );                    // shared ownership
//]
}
//----------------------------------------------------------

} // set2

//----------------------------------------------------------
//[doc_storage_ptr_10
class logging_resource : public boost::container::pmr::memory_resource
{
private:
    void* do_allocate( std::size_t bytes, std::size_t align ) override
    {
        std::cout << "Allocating " << bytes << " bytes with alignment " << align << '\n';

        return ::operator new( bytes );
    }

    void do_deallocate( void* ptr, std::size_t bytes, std::size_t align ) override
    {
        std::cout << "Deallocating " << bytes << " bytes with alignment " << align << " @ address " << ptr << '\n';

        return ::operator delete( ptr );
    }

    bool do_is_equal( memory_resource const& other ) const noexcept override
    {
        // since the global allocation and deallocation functions are used,
        // any instance of a logging_resource can deallocate memory allocated
        // by another instance of a logging_resource

        return dynamic_cast< logging_resource const* >( &other ) != nullptr;
    }
};
//]

//----------------------------------------------------------

class doc_storage_ptr_test
{
public:
    void
    run()
    {
        (void)&set1;
        BOOST_TEST_PASS();
    }
};

TEST_SUITE(doc_storage_ptr_test, "boost.json.doc_storage_ptr");

} // namespace json
} // namespace boost
