#include "device_bus/manager/SerialDeviceManager.h"

#include <QJsonDocument>
#include <QSerialPort>

#include "device_bus/tree/DeviceTreeModel.h"
#include "devices/DeviceTypes.h"
#include "hardware/drivers/serial/SerialCapabilities.h"
#include "hardware/drivers/serial/SerialQtDriver.h"
#include "message_bus/egress/EgressRouter.h"
#include "message_bus/ingress/DriverIngressAdapter.h"
#include "utils/logs/logging.h"

using softbus::hardware::drivers::serial::SerialQtDriver;
namespace serial_cap = softbus::hardware::drivers::serial::capabilities;

namespace softbus::device_bus::manager
{

static bool setIfChanged(QJsonObject& params, const QString& key, const QJsonValue& value)
{
    const auto it = params.find(key);
    if (it == params.end() || it.value() != value) {
        params.insert(key, value);
        return true;
    }
    return false;
}

static bool persistSerialParams(DeviceTreeModel* treeModel,
                                const QString& endpoint,
                                const QJsonObject& mergedParams,
                                const std::function<void()>& dirtyCb)
{
    if (!treeModel || endpoint.isEmpty() || mergedParams.isEmpty()) {
        return false;
    }

    const int idx = treeModel->findIndexByEndpoint(endpoint);
    if (idx < 0) {
        return false;
    }

    auto& nodes = treeModel->nodesMutable();
    if (idx >= nodes.size()) {
        return false;
    }

    auto& node = nodes[idx];
    auto& p = node.params;

    bool changed = false;
    changed = setIfChanged(p, QStringLiteral("baudRate"), mergedParams.value(QStringLiteral("baudRate"))) || changed;
    changed = setIfChanged(p, QStringLiteral("dataBits"), mergedParams.value(QStringLiteral("dataBits"))) || changed;
    changed = setIfChanged(p, QStringLiteral("parity"), mergedParams.value(QStringLiteral("parity"))) || changed;
    changed = setIfChanged(p, QStringLiteral("stopBits"), mergedParams.value(QStringLiteral("stopBits"))) || changed;
    changed =
        setIfChanged(p, QStringLiteral("flowControl"), mergedParams.value(QStringLiteral("flowControl"))) || changed;

    if (changed && dirtyCb) {
        dirtyCb();
    }
    return changed;
}

bool initializeSerialDeviceWithDeps(const QString& endpoint, const QJsonObject& params, const SerialInitDeps& deps)
{
    if (!deps.serialDevicesByEndpoint) {
        return false;
    }

    if (deps.serialDevicesByEndpoint->contains(endpoint)) {
        LOG_WARNING() << "DeviceBusManager: serial device already exists for" << endpoint;
        return false;
    }

    serial_cap::SerialDefaults def = serial_cap::kDefaults;

    int baud = params.value(QStringLiteral("baudRate")).toInt(def.baudRate);
    if (!serial_cap::isValidBaud(baud)) baud = def.baudRate;

    int dataBits = params.value(QStringLiteral("dataBits")).toInt(def.dataBits);
    if (!serial_cap::isValidDataBits(dataBits)) dataBits = def.dataBits;

    QString parityStr = params.value(QStringLiteral("parity")).toString(QLatin1String(def.parity));
    if (!serial_cap::isValidParity(parityStr)) parityStr = QLatin1String(def.parity);

    int stopBitsVal = params.value(QStringLiteral("stopBits")).toInt(def.stopBits);
    if (!serial_cap::isValidStopBits(stopBitsVal)) stopBitsVal = def.stopBits;

    QString flowStr = params.value(QStringLiteral("flowControl")).toString(QLatin1String(def.flowControl));
    if (!serial_cap::isValidFlowControl(flowStr)) flowStr = QLatin1String(def.flowControl);

    QJsonObject merged;
    merged.insert(QStringLiteral("baudRate"), baud);
    merged.insert(QStringLiteral("dataBits"), dataBits);
    merged.insert(QStringLiteral("parity"), parityStr);
    merged.insert(QStringLiteral("stopBits"), stopBitsVal);
    merged.insert(QStringLiteral("flowControl"), flowStr);

    LOG_INFO() << "DeviceBusManager: init serial endpoint=" << endpoint
               << "params=" << QJsonDocument(merged).toJson(QJsonDocument::Compact);

    persistSerialParams(deps.treeModel, endpoint, merged, deps.dirtyCb);

    auto driver = std::make_unique<SerialQtDriver>(nullptr);
    driver->SetBaudRate(static_cast<QSerialPort::BaudRate>(baud));
    driver->SetDataBits(static_cast<QSerialPort::DataBits>(dataBits));

    QSerialPort::Parity parity = QSerialPort::NoParity;
    if (parityStr == QStringLiteral("even")) {
        parity = QSerialPort::EvenParity;
    } else if (parityStr == QStringLiteral("odd")) {
        parity = QSerialPort::OddParity;
    }
    driver->SetParity(parity);

    QSerialPort::StopBits stopBits = (stopBitsVal == 2) ? QSerialPort::TwoStop : QSerialPort::OneStop;
    driver->SetStopBits(stopBits);

    QSerialPort::FlowControl flow = QSerialPort::NoFlowControl;
    if (flowStr == QStringLiteral("hardware")) {
        flow = QSerialPort::HardwareControl;
    } else if (flowStr == QStringLiteral("software")) {
        flow = QSerialPort::SoftwareControl;
    }
    driver->SetFlowControl(flow);

    softbus::devices::DeviceInfo info;
    info.id = -1;
    info.endpoint = endpoint;
    info.type = QStringLiteral("serial");
    info.kind = softbus::devices::DeviceKind::Serial;

    softbus::devices::physical::SerialDevice::Config cfg;
    cfg.endpoint = endpoint;
    cfg.baudRate = baud;
    cfg.dataBits = dataBits;
    cfg.parity = parityStr;
    cfg.stopBits = stopBitsVal;
    cfg.flowControl = flowStr;

    QSharedPointer<softbus::devices::physical::SerialDevice> device(
        new softbus::devices::physical::SerialDevice(std::move(driver), cfg, info));
    // 绑定 ingress 适配器和内存池 数据推送到 ingress 队列
    device->bindIngress(deps.ingressAdapter, deps.memoryPool);

    if (deps.registry) {
        deps.registry->registerDevice(info);
    }

    if (!device->start()) {
        LOG_ERROR() << "DeviceBusManager: failed to start serial device for" << endpoint;
        return false;
    }

    deps.serialDevicesByEndpoint->insert(endpoint, device);
    // 暂时不绑定 egress 路由器 因为还没有 egress 路由器
    // egress 路由器 是用来将数据从 ingress 队列中取出并发送给设备的
    // if (deps.egressRouter) {
    //     deps.egressRouter->bindSerialDevice(endpoint, device);
    // }

    return true;
}

} // namespace softbus::device_bus::manager
