#ifdef __cplusplus
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <stdexcept>
#include <vector>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "dcmi_interface_api.h"

#define NPU_OK (0)
#define MAX_CARD_NUM (16)

#ifndef DCMI_QOS_CFG_RESERVED_LEN
#define DCMI_QOS_CFG_RESERVED_LEN 16
#endif

#define BITMAP_ARRAY_LENGTH     4
#define PCIE_MASTER_ID     7

int set_gbl_qos(int card_id, int device_id, int mode)
{
    struct dcmi_qos_gbl_config gblCfg = { 0 };
    gblCfg.enable = 1;
    gblCfg.autoqos_fuse_en = 1;
    gblCfg.mpamqos_fuse_mode = mode;

    int ret = dcmi_set_device_info(
        card_id,
        device_id,
        DCMI_MAIN_CMD_QOS,
        (unsigned int)DCMI_QOS_SUB_GLOBAL_CONFIG,
        (const void*)(&gblCfg),
        (unsigned int)sizeof(struct dcmi_qos_gbl_config)
    );
    if (ret != 0) {
        printf("[ERROR] Failed to set QoS global configuration for card %d - device %d, error code: %d\n",
            card_id, device_id, ret);
        return ret;
    } else {
        printf("[SUCCESS] Succeeded in setting QoS global configuration for card %d - device %d, mode = %d \n",
            card_id, device_id, mode);
    }

    return 0;
}

int set_bw(int target, unsigned int bw_low, unsigned int bw_high, int hardlimit, int card_id, int device_id)
{
    if (card_id < 0 || device_id < 0) {
        printf("invalid card_id(%d) or device_id(%d)\n", card_id, device_id);
        return -1;
    }
    if (hardlimit < 0 || hardlimit > 1) {
        printf("hardlimit must be 0 or 1\n");
        return -1;
    }

    struct dcmi_qos_mata_config mataCfg = { 0 };

    mataCfg.mpamid = target;
    mataCfg.bw_high = bw_high;
    mataCfg.bw_low = bw_low;
    mataCfg.hardlimit = hardlimit;

    for (int i = 0; i < DCMI_QOS_CFG_RESERVED_LEN; i++) {
        mataCfg.reserved[i] = 0;
    }

    int ret = dcmi_set_device_info(
        card_id,
        device_id,
        DCMI_MAIN_CMD_QOS,
        (unsigned int)DCMI_QOS_SUB_MATA_CONFIG,
        (const void*)(&mataCfg),
        (unsigned int)sizeof(struct dcmi_qos_mata_config)
    );
    if (ret != 0) {
        printf("[card:%d, dev:%d] set mata qos config failed, ret = %d\n",
            card_id, device_id, ret);
        return ret;
    }

    printf("[card:%d, dev:%d] set mata qos config success\n", card_id, device_id);
    return 0;
}

int get_device_id_in_card()
{
    int ret;
    int device_id_max = 0;
    int mcu_id = 0;
    int cpu_id = 0;
    int card_id = 0;
    ret = dcmi_get_device_id_in_card(card_id, &device_id_max, &mcu_id, &cpu_id);
    if (ret != 0) {
        printf("[dev:%d]set mata qos config failed, ret = %d\n", 0, ret);
    }

    printf("card:%d 的device:%d\n", card_id, device_id_max);
    return 0;
}

int get_card_list(int *card_num, int *card_list, int max_len)
{
    if (card_num == NULL || card_list == NULL || max_len <= 0) {
        printf("get_card_list: invalid input parameter\n");
        return -1;
    }

    *card_num = 0;
    for (int i = 0; i < max_len; i++) {
        card_list[i] = 0;
    }

    int ret = dcmi_get_card_list(card_num, card_list, max_len);
    if (ret != 0) {
        printf("get card list fail, ret = %d\n", ret);
        return ret;
    }

    return 0;
}

int qos_init()
{
    int ret = dcmi_init();
    if (ret != NPU_OK) {
        printf("Failed to init dcmi.\n");
        return ret;
    }

    return 0;
}

int set_h2d_qos(int card_id, int device_id, int mpamid, int qos, unsigned long long bitmap[BITMAP_ARRAY_LENGTH])
{
    struct dcmi_qos_master_config masterConfig = {0};
    masterConfig.master = PCIE_MASTER_ID;
    masterConfig.mpamid = mpamid;
    masterConfig.qos = qos;
    for (int i = 0; i < BITMAP_ARRAY_LENGTH; i++) {
        masterConfig.bitmap[i] = bitmap[i];
    }

    int ret = dcmi_set_device_info(
        card_id,
        device_id,
        DCMI_MAIN_CMD_QOS,
        (unsigned int)DCMI_QOS_SUB_MASTER_CONFIG,
        (const void*)(&masterConfig),
        (unsigned int)sizeof(struct dcmi_qos_master_config)
    );
    if (ret != 0) {
        printf("[ERROR] Failed to set H2D QoS for card %d - device %d, error code: %d\n",
            card_id, device_id, ret);
        return ret;
    } else {
        printf("[SUCCESS] Succeeded in setting H2D QoS for card %d - device %d, qos = %d \n", card_id, device_id, qos);
    }
    return 0;
}

namespace py = pybind11;

PYBIND11_MODULE(aiQos, m)
{
    m.doc() = "AI QoS (Quality of Service) control module for hardware resource management";
    m.def(
        "set_bw",
        &set_bw,
        py::arg("target"),
        py::arg("bw_low"),
        py::arg("bw_high"),
        py::arg("hardlimit"),
        py::arg("card_id"),
        py::arg("device_id")
    );
    m.def(
        "init",
        &qos_init);
    m.def(
        "set_gbl_qos",
        &set_gbl_qos,
        py::arg("card_id"),
        py::arg("device_id"),
        py::arg("mode")
    );
    m.def(
        "set_h2d_qos",
        [](int card_id, int device_id, int mpamid, int qos, const std::vector<unsigned long long>& bitmap_vec) {
            if (bitmap_vec.size() != BITMAP_ARRAY_LENGTH) {
                throw py::value_error("Bitmap must be a list of exactly 4 integers. Current count: " + std::to_string(bitmap_vec.size()));
            }
            unsigned long long bitmap[BITMAP_ARRAY_LENGTH];
            for (int i = 0; i < BITMAP_ARRAY_LENGTH; ++i) {
                bitmap[i] = bitmap_vec[i];
            }
            return set_h2d_qos(card_id, device_id, mpamid, qos, bitmap);
        },
        py::arg("card_id"),
        py::arg("device_id"),
        py::arg("mpamid"),
        py::arg("qos"),
        py::arg("bitmap")
    );
}