// {
//   account: [<account>|<account_public_key>]
//   peer: [<account>|<account_public_key>]
//   currency: <currency>
//   ledger_hash : <ledger>
//   ledger_index : <ledger_index>
// }
json::value doaccountasset (rpc::context& context)
{
    auto const& params(context.params);
    if (!params.ismember(jss::account))
        return rpc::missing_field_error("account");
    if (!params.ismember(jss::peer))
        return rpc::missing_field_error("peer");
    if (!params.ismember(jss::currency))
        return rpc::missing_field_error("currency");

    ledger::pointer ledger;
    json::value result(rpc::lookupledger(params, ledger, context.netops));
    if (!ledger)
        return result;

    std::string strident(params[jss::account].asstring());
    bool bindex(params.ismember(jss::account_index));
    int iindex(bindex ? params[jss::account_index].asuint() : 0);
    rippleaddress rippleaddress;

    json::value const jv(rpc::accountfromstring(ledger, rippleaddress, bindex,
                                                strident, iindex, false, context.netops));
    if (!jv.empty()) {
        for (json::value::const_iterator it(jv.begin()); it != jv.end(); ++it)
            result[it.membername()] = it.key();

        return result;
    }

    if (!ledger->hasaccount(rippleaddress))
        return rpcerror(rpcact_not_found);

    std::string strpeer(params.ismember(jss::peer) ? params[jss::peer].asstring() : "");
    bool bpeerindex(params.ismember(jss::peer_index));
    int ipeerindex(bindex ? params[jss::peer_index].asuint() : 0);
    rippleaddress rippleaddresspeer;

    json::value const jvpeer(rpc::accountfromstring(ledger, rippleaddresspeer, bpeerindex,
                                                    strpeer, ipeerindex, false, context.netops));
    if (!jvpeer.empty()) {
        return result;
    }

    if (!ledger->hasaccount(rippleaddresspeer))
        return rpcerror(rpcact_not_found);

    currency ucurrency;
    if (!to_currency(ucurrency, params[jss::currency].asstring()))
        return rpc::make_error(rpcsrc_cur_malformed, "invalid field 'currency', bad currency.");

    account const& raaccount(rippleaddress.getaccountid());
    account const& rapeeraccount(rippleaddresspeer.getaccountid());

    uint256 unodeindex = getripplestateindex(raaccount, rapeeraccount, ucurrency);
    auto slenode = context.netops.getslei(ledger, unodeindex);
    auto const line(ripplestate::makeitem(raaccount, slenode));

    if (line == nullptr ||
        raaccount != line->getaccountid() ||
        rapeeraccount != line->getaccountidpeer())
        return result;

    json::value jsonlines(json::arrayvalue);
    addline(jsonlines, *line, ledger);
    result[jss::lines] = jsonlines[0u];
    
    json::value& jsonassetstates(result[jss::states] = json::arrayvalue);
    
    // get asset_states for currency asset.
    if (assetcurrency() == line->getbalance().getcurrency()) {
        auto lpledger = std::make_shared<ledger> (std::ref (*ledger), false);
        ledgerentryset les(lpledger, tapnone);
        auto sleripplestate = les.entrycache(ltripple_state, getripplestateindex(raaccount, rapeeraccount, assetcurrency()));
        les.assetrelease(raaccount, rapeeraccount, assetcurrency(), sleripplestate);

        uint256 baseindex = getassetstateindex(line->getaccountid(), line->getaccountidpeer(), assetcurrency());
        uint256 assetstateindex = getqualityindex(baseindex);
        uint256 assetstateend = getqualitynext(assetstateindex);

        for (;;) {
            auto const& sle = les.entrycache(ltasset_state, assetstateindex);
            if (sle) {
                stamount amount = sle->getfieldamount(sfamount);
                stamount released = sle->getfieldamount(sfdeliveredamount);

                if (sle->getfieldaccount160(sfaccount) == line->getaccountidpeer()) {
                    amount.negate();
                    released.negate();
                }

                auto reserved = released ? amount - released : amount;

                json::value& state(jsonassetstates.append(json::objectvalue));
                state[jss::date] = static_cast<json::uint>(getquality(assetstateindex));
                state[jss::amount] = amount.gettext();
                state[jss::reserve] = reserved.gettext();
            }

            auto const nextassetstate(
                les.getnextledgerindex(assetstateindex, assetstateend));

            if (nextassetstate.iszero())
                break;

            assetstateindex = nextassetstate;
        }
    }

    context.loadtype = resource::feemediumburdenrpc;
    return result;
}
// {
//   account: <account>|<account_public_key>
//   account_index: <number>        // optional, defaults to 0.
//   ledger_hash : <ledger>
//   ledger_index : <ledger_index>
//   limit: integer                 // optional
//   marker: opaque                 // optional, resume previous query
// }
json::value doaccountlines (rpc::context& context)
{
    auto const& params (context.params);
    if (! params.ismember (jss::account))
        return rpc::missing_field_error ("account");

    ledger::pointer ledger;
    json::value result (rpc::lookupledger (params, ledger, context.netops));
    if (! ledger)
        return result;

    std::string strident (params[jss::account].asstring ());
    bool bindex (params.ismember (jss::account_index));
    int iindex (bindex ? params[jss::account_index].asuint () : 0);
    rippleaddress rippleaddress;

    json::value const jv (rpc::accountfromstring (ledger, rippleaddress, bindex,
        strident, iindex, false, context.netops));
    if (! jv.empty ())
    {
        for (json::value::const_iterator it (jv.begin ()); it != jv.end (); ++it)
            result[it.membername ()] = it.key ();

        return result;
    }

    if (! ledger->hasaccount (rippleaddress))
        return rpcerror (rpcact_not_found);

    std::string strpeer (params.ismember (jss::peer)
        ? params[jss::peer].asstring () : "");
    bool bpeerindex (params.ismember (jss::peer_index));
    int ipeerindex (bindex ? params[jss::peer_index].asuint () : 0);

    rippleaddress rippleaddresspeer;

    if (! strpeer.empty ())
    {
        result[jss::peer] = rippleaddress.humanaccountid ();

        if (bpeerindex)
            result[jss::peer_index] = ipeerindex;

        result = rpc::accountfromstring (ledger, rippleaddresspeer, bpeerindex,
            strpeer, ipeerindex, false, context.netops);

        if (! result.empty ())
            return result;
    }

    account rapeeraccount;
    if (rippleaddresspeer.isvalid ())
        rapeeraccount = rippleaddresspeer.getaccountid ();

    unsigned int limit;
    if (params.ismember (jss::limit))
    {
        auto const& jvlimit (params[jss::limit]);
        if (! jvlimit.isintegral ())
            return rpc::expected_field_error ("limit", "unsigned integer");

        limit = jvlimit.isuint () ? jvlimit.asuint () :
            std::max (0, jvlimit.asint ());

        if (context.role != role::admin)
        {
            limit = std::max (rpc::tuning::minlinesperrequest,
                std::min (limit, rpc::tuning::maxlinesperrequest));
        }
    }
    else
    {
        limit = rpc::tuning::defaultlinesperrequest;
    }

    json::value& jsonlines (result[jss::lines] = json::arrayvalue);
    account const& raaccount(rippleaddress.getaccountid ());
    visitdata visitdata = { {}, raaccount, rippleaddresspeer, rapeeraccount };
    unsigned int reserve (limit);
    uint256 startafter;
    std::uint64_t starthint;

    if (params.ismember (jss::marker))
    {
        // we have a start point. use limit - 1 from the result and use the
        // very last one for the resume.
        json::value const& marker (params[jss::marker]);

        if (! marker.isstring ())
            return rpc::expected_field_error ("marker", "string");

        startafter.sethex (marker.asstring ());
        sle::pointer sleline (ledger->getslei (startafter));

        if (sleline == nullptr || sleline->gettype () != ltripple_state)
            return rpcerror (rpcinvalid_params);

        if (sleline->getfieldamount (sflowlimit).getissuer () == raaccount)
            starthint = sleline->getfieldu64 (sflownode);
        else if (sleline->getfieldamount (sfhighlimit).getissuer () == raaccount)
            starthint = sleline->getfieldu64 (sfhighnode);
        else
            return rpcerror (rpcinvalid_params);

        // caller provided the first line (startafter), add it as first result
        auto const line (ripplestate::makeitem (raaccount, sleline));
        if (line == nullptr)
            return rpcerror (rpcinvalid_params);

        addline (jsonlines, *line, ledger);
        visitdata.items.reserve (reserve);
    }
    else
    {
        starthint = 0;
        // we have no start point, limit should be one higher than requested.
        visitdata.items.reserve (++reserve);
    }

    if (! ledger->visitaccountitems (raaccount, startafter, starthint, reserve,
        [&visitdata](sle::ref slecur)
        {
            auto const line (ripplestate::makeitem (visitdata.accountid, slecur));
            if (line != nullptr &&
                (! visitdata.rippleaddresspeer.isvalid () ||
                visitdata.rapeeraccount == line->getaccountidpeer ()))
            {
                visitdata.items.emplace_back (line);
                return true;
            }

            return false;
        }))
    {
        return rpcerror (rpcinvalid_params);
    }

    if (visitdata.items.size () == reserve)
    {
        result[jss::limit] = limit;

        ripplestate::pointer line (visitdata.items.back ());
        result[jss::marker] = to_string (line->peeksle ().getindex ());
        visitdata.items.pop_back ();
    }

    result[jss::account] = rippleaddress.humanaccountid ();

    for (auto const& item : visitdata.items)
        addline (jsonlines, *item.get (), ledger);

    context.loadtype = resource::feemediumburdenrpc;
    return result;
}
// {
//   account: <account>|<account_public_key>
//   account_index: <number>        // optional, defaults to 0.
//   ledger_hash : <ledger>
//   ledger_index : <ledger_index>
//   limit: integer                 // optional
//   marker: opaque                 // optional, resume previous query
// }
json::value doaccountoffers (rpc::context& context)
{
    auto const& params (context.params);
    if (! params.ismember (jss::account))
        return rpc::missing_field_error ("account");

    ledger::pointer ledger;
    json::value result (rpc::lookupledger (params, ledger, context.netops));
    if (! ledger)
        return result;

    std::string strident (params[jss::account].asstring ());
    bool bindex (params.ismember (jss::account_index));
    int const iindex (bindex ? params[jss::account_index].asuint () : 0);
    rippleaddress rippleaddress;

    json::value const jv (rpc::accountfromstring (ledger, rippleaddress, bindex,
        strident, iindex, false, context.netops));
    if (! jv.empty ())
    {
        for (json::value::const_iterator it (jv.begin ()); it != jv.end (); ++it)
            result[it.membername ()] = it.key ();

        return result;
    }

    // get info on account.
    result[jss::account] = rippleaddress.humanaccountid ();

    if (bindex)
        result[jss::account_index] = iindex;

    if (! ledger->hasaccount (rippleaddress))
        return rpcerror (rpcact_not_found);

    unsigned int limit;
    if (params.ismember (jss::limit))
    {
        auto const& jvlimit (params[jss::limit]);
        if (! jvlimit.isintegral ())
            return rpc::expected_field_error ("limit", "unsigned integer");

        limit = jvlimit.isuint () ? jvlimit.asuint () :
            std::max (0, jvlimit.asint ());

        if (context.role != role::admin)
        {
            limit = std::max (rpc::tuning::minoffersperrequest,
                std::min (limit, rpc::tuning::maxoffersperrequest));
        }
    }
    else
    {
        limit = rpc::tuning::defaultoffersperrequest;
    }

    account const& raaccount (rippleaddress.getaccountid ());
    json::value& jsonoffers (result[jss::offers] = json::arrayvalue);
    std::vector <sle::pointer> offers;
    unsigned int reserve (limit);
    uint256 startafter;
    std::uint64_t starthint;

    if (params.ismember(jss::marker))
    {
        // we have a start point. use limit - 1 from the result and use the
        // very last one for the resume.
        json::value const& marker (params[jss::marker]);

        if (! marker.isstring ())
            return rpc::expected_field_error ("marker", "string");

        startafter.sethex (marker.asstring ());
        sle::pointer sleoffer (ledger->getslei (startafter));

        if (sleoffer == nullptr ||
            sleoffer->gettype () != ltoffer ||
            raaccount != sleoffer->getfieldaccount160 (sfaccount))
        {
            return rpcerror (rpcinvalid_params);
        }

        starthint = sleoffer->getfieldu64(sfownernode);

        // caller provided the first offer (startafter), add it as first result
        json::value& obj (jsonoffers.append (json::objectvalue));
        sleoffer->getfieldamount (sftakerpays).setjson (obj[jss::taker_pays]);
        sleoffer->getfieldamount (sftakergets).setjson (obj[jss::taker_gets]);
        obj[jss::seq] = sleoffer->getfieldu32 (sfsequence);
        obj[jss::flags] = sleoffer->getfieldu32 (sfflags);

        offers.reserve (reserve);
    }
    else
    {
        starthint = 0;
        // we have no start point, limit should be one higher than requested.
        offers.reserve (++reserve);
    }

    if (! ledger->visitaccountitems (raaccount, startafter, starthint, reserve,
        [&offers](sle::ref offer)
        {
            if (offer->gettype () == ltoffer)
            {
                offers.emplace_back (offer);
                return true;
            }

            return false;
        }))
    {
        return rpcerror (rpcinvalid_params);
    }

    if (offers.size () == reserve)
    {
        result[jss::limit] = limit;

        result[jss::marker] = to_string (offers.back ()->getindex ());
        offers.pop_back ();
    }

    for (auto const& offer : offers)
    {
        json::value& obj (jsonoffers.append (json::objectvalue));
        offer->getfieldamount (sftakerpays).setjson (obj[jss::taker_pays]);
        offer->getfieldamount (sftakergets).setjson (obj[jss::taker_gets]);
        obj[jss::seq] = offer->getfieldu32 (sfsequence);
        obj[jss::flags] = offer->getfieldu32 (sfflags);
    }

    context.loadtype = resource::feemediumburdenrpc;
    return result;
}