// Copyright (c) 2014-2026 Savoir-faire Linux Inc.
// SPDX-License-Identifier: MIT

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "callbacks.h"

namespace dht {

GetCallbackSimple
bindGetCb(GetCallbackRaw raw_cb, void* user_data)
{
    if (not raw_cb)
        return {};
    return [=](const std::shared_ptr<Value>& value) {
        return raw_cb(value, user_data);
    };
}

GetCallback
bindGetCb(GetCallbackSimple cb)
{
    if (not cb)
        return {};
    return [cb = std::move(cb)](const std::vector<std::shared_ptr<Value>>& values) {
        for (const auto& v : values)
            if (not cb(v))
                return false;
        return true;
    };
}

ValueCallback
bindValueCb(ValueCallbackRaw raw_cb, void* user_data)
{
    if (not raw_cb)
        return {};
    return [=](const std::vector<std::shared_ptr<Value>>& values, bool expired) {
        for (const auto& v : values)
            if (not raw_cb(v, expired, user_data))
                return false;
        return true;
    };
}

ShutdownCallback
bindShutdownCb(ShutdownCallbackRaw shutdown_cb_raw, void* user_data)
{
    return [=]() {
        shutdown_cb_raw(user_data);
    };
}

DoneCallback
bindDoneCb(DoneCallbackSimple donecb)
{
    if (not donecb)
        return {};
    using namespace std::placeholders;
    return std::bind(std::move(donecb), _1);
}

DoneCallback
bindDoneCb(DoneCallbackRaw raw_cb, void* user_data)
{
    if (not raw_cb)
        return {};
    return [=](bool success, const std::vector<std::shared_ptr<Node>>& nodes) {
        raw_cb(success, (std::vector<std::shared_ptr<Node>>*) &nodes, user_data);
    };
}

DoneCallbackSimple
bindDoneCbSimple(DoneCallbackSimpleRaw raw_cb, void* user_data)
{
    if (not raw_cb)
        return {};
    return [=](bool success) {
        raw_cb(success, user_data);
    };
}

std::string
NodeStats::toString() const
{
    std::ostringstream ss;
    ss << "Known nodes: " << good_nodes << " good, " << dubious_nodes << " dubious, " << incoming_nodes << " incoming."
       << std::endl;
    ss << searches << " searches, " << node_cache_size << " total cached nodes" << std::endl;
    if (table_depth > 1) {
        ss << "Routing table depth: " << table_depth << std::endl;
        ss << "Network size estimation: " << getNetworkSizeEstimation() << " nodes" << std::endl;
    }
    return ss.str();
}

#ifdef OPENDHT_JSONCPP
/**
 * Build a json object from a NodeStats
 */
Json::Value
NodeStats::toJson() const
{
    Json::Value val;
    val["good"] = static_cast<Json::LargestUInt>(good_nodes);
    val["dubious"] = static_cast<Json::LargestUInt>(dubious_nodes);
    val["incoming"] = static_cast<Json::LargestUInt>(incoming_nodes);
    if (table_depth > 1) {
        val["table_depth"] = static_cast<Json::LargestUInt>(table_depth);
        val["network_size_estimation"] = static_cast<Json::LargestUInt>(getNetworkSizeEstimation());
    }
    return val;
}

NodeStats::NodeStats(const Json::Value& val)
{
    if (val.isMember("good"))
        good_nodes = static_cast<unsigned>(val["good"].asLargestUInt());
    if (val.isMember("dubious"))
        dubious_nodes = static_cast<unsigned>(val["dubious"].asLargestUInt());
    if (val.isMember("incoming"))
        incoming_nodes = static_cast<unsigned>(val["incoming"].asLargestUInt());
    if (val.isMember("table_depth"))
        table_depth = static_cast<unsigned>(val["table_depth"].asLargestUInt());
}

/**
 * Build a json object from a NodeStats
 */
Json::Value
NodeInfo::toJson() const
{
    Json::Value val;
    if (id)
        val["id"] = id.toString();
    val["node_id"] = node_id.toString();
    val["ipv4"] = ipv4.toJson();
    val["ipv6"] = ipv6.toJson();
    val["ops"] = Json::Value::LargestUInt(ongoing_ops);
    val["storage_values"] = Json::Value::LargestUInt(storage_values);
    val["storage_size"] = Json::Value::LargestUInt(storage_size);
    val["local_storage_values"] = Json::Value::LargestUInt(local_storage_values);
    val["local_storage_size"] = Json::Value::LargestUInt(local_storage_size);
    val["port_ipv4"] = Json::Value::LargestUInt(bound4);
    val["port_ipv6"] = Json::Value::LargestUInt(bound6);
    return val;
}

NodeInfo::NodeInfo(const Json::Value& v)
{
    if (v.isMember("id"))
        id = InfoHash(v["id"].asString());
    node_id = InfoHash(v["node_id"].asString());
    ipv4 = NodeStats(v["ipv4"]);
    ipv6 = NodeStats(v["ipv6"]);
    ongoing_ops = v["ops"].asLargestUInt();
    storage_values = v["storage_values"].asLargestUInt();
    storage_size = v["storage_size"].asLargestUInt();
    local_storage_values = v["local_storage_values"].asLargestUInt();
    local_storage_size = v["local_storage_size"].asLargestUInt();
    bound4 = v["port_ipv4"].asLargestUInt();
    bound6 = v["port_ipv6"].asLargestUInt();
}

#endif

} // namespace dht
