#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/resource/Charge.h>
#include <xrpl/resource/Consumer.h>
#include <xrpl/resource/Disposition.h>
#include <xrpl/resource/detail/Entry.h>
#include <xrpl/resource/detail/Logic.h>

#include <ostream>
#include <string>

namespace xrpl {
namespace Resource {

Consumer::Consumer(Logic& logic, Entry& entry) : m_logic(&logic), m_entry(&entry)
{
}

Consumer::Consumer() : m_logic(nullptr), m_entry(nullptr)
{
}

Consumer::Consumer(Consumer const& other) : m_logic(other.m_logic), m_entry(nullptr)
{
    if ((m_logic != nullptr) && (other.m_entry != nullptr))
    {
        m_entry = other.m_entry;
        m_logic->acquire(*m_entry);
    }
}

Consumer::~Consumer()
{
    if ((m_logic != nullptr) && (m_entry != nullptr))
        m_logic->release(*m_entry);
}

Consumer&
Consumer::operator=(Consumer const& other)
{
    if (this == &other)
        return *this;

    // remove old ref
    if ((m_logic != nullptr) && (m_entry != nullptr))
        m_logic->release(*m_entry);

    m_logic = other.m_logic;
    m_entry = other.m_entry;

    // add new ref
    if ((m_logic != nullptr) && (m_entry != nullptr))
        m_logic->acquire(*m_entry);

    return *this;
}

std::string
Consumer::to_string() const
{
    if (m_logic == nullptr)
        return "(none)";

    return m_entry->to_string();
}

bool
Consumer::isUnlimited() const
{
    if (m_entry != nullptr)
        return m_entry->isUnlimited();

    return false;
}

Disposition
Consumer::disposition() const
{
    Disposition d = ok;
    if ((m_logic != nullptr) && (m_entry != nullptr))
        d = m_logic->charge(*m_entry, Charge(0));

    return d;
}

Disposition
Consumer::charge(Charge const& what, std::string const& context)
{
    Disposition d = ok;

    if ((m_logic != nullptr) && (m_entry != nullptr) && !m_entry->isUnlimited())
        d = m_logic->charge(*m_entry, what, context);

    return d;
}

bool
Consumer::warn()
{
    XRPL_ASSERT(m_entry, "xrpl::Resource::Consumer::warn : non-null entry");
    return m_logic->warn(*m_entry);
}

bool
Consumer::disconnect(beast::Journal const& j)
{
    XRPL_ASSERT(m_entry, "xrpl::Resource::Consumer::disconnect : non-null entry");
    bool const d = m_logic->disconnect(*m_entry);
    if (d)
    {
        JLOG(j.debug()) << "disconnecting " << m_entry->to_string();
    }
    return d;
}

int
Consumer::balance()
{
    XRPL_ASSERT(m_entry, "xrpl::Resource::Consumer::balance : non-null entry");
    return m_logic->balance(*m_entry);
}

Entry&
Consumer::entry()
{
    XRPL_ASSERT(m_entry, "xrpl::Resource::Consumer::entry : non-null entry");
    return *m_entry;
}

void
Consumer::setPublicKey(PublicKey const& publicKey)
{
    m_entry->publicKey = publicKey;
}

std::ostream&
operator<<(std::ostream& os, Consumer const& v)
{
    os << v.to_string();
    return os;
}

}  // namespace Resource
}  // namespace xrpl
