#pragma once

#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/protocol/AccountID.h>

namespace xrpl {
namespace detail {

class CurrencyTag
{
public:
    explicit CurrencyTag() = default;
};

class DirectoryTag
{
public:
    explicit DirectoryTag() = default;
};

class NodeIDTag
{
public:
    explicit NodeIDTag() = default;
};

}  // namespace detail

/** Directory is an index into the directory of offer books.
    The last 64 bits of this are the quality. */
using Directory = base_uint<256, detail::DirectoryTag>;

/** Currency is a hash representing a specific currency. */
using Currency = base_uint<160, detail::CurrencyTag>;

/** NodeID is a 160-bit hash representing one node. */
using NodeID = base_uint<160, detail::NodeIDTag>;

/** MPTID is a 192-bit value representing MPT Issuance ID,
 * which is a concatenation of a 32-bit sequence (big endian)
 * and a 160-bit account */
using MPTID = base_uint<192>;

/** Domain is a 256-bit hash representing a specific domain. */
using Domain = base_uint<256>;

/** XRP currency. */
Currency const&
xrpCurrency();

/** A placeholder for empty currencies. */
Currency const&
noCurrency();

/** We deliberately disallow the currency that looks like "XRP" because too
    many people were using it instead of the correct XRP currency. */
Currency const&
badCurrency();

inline bool
isXRP(Currency const& c)
{
    return c == beast::zero;
}

/** Returns "", "XRP", or three letter ISO code. */
std::string
to_string(Currency const& c);

/** Tries to convert a string to a Currency, returns true on success.

    @note This function will return success if the resulting currency is
          badCurrency(). This legacy behavior is unfortunate; changing this
          will require very careful checking everywhere and may mean having
          to rewrite some unit test code.
*/
bool
to_currency(Currency&, std::string const&);

/** Tries to convert a string to a Currency, returns noCurrency() on failure.

    @note This function can return badCurrency(). This legacy behavior is
          unfortunate; changing this will require very careful checking
          everywhere and may mean having to rewrite some unit test code.
*/
Currency
to_currency(std::string const&);

inline std::ostream&
operator<<(std::ostream& os, Currency const& x)
{
    os << to_string(x);
    return os;
}

}  // namespace xrpl

namespace std {

template <>
struct hash<xrpl::Currency> : xrpl::Currency::hasher
{
    hash() = default;
};

template <>
struct hash<xrpl::NodeID> : xrpl::NodeID::hasher
{
    hash() = default;
};

template <>
struct hash<xrpl::Directory> : xrpl::Directory::hasher
{
    hash() = default;
};

template <>
struct hash<xrpl::uint256> : xrpl::uint256::hasher
{
    hash() = default;
};

}  // namespace std
