/* Copyright (c) 2024 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#ifndef L_SML_CALLBACK
#define L_SML_CALLBACK

#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <mutex>
#include <thread>
#include <map>

#include "l_sml.h"

namespace sml {
using t_skynet_send = int (*)(struct skynet_context *context, uint32_t source, uint32_t destination, int type,
                              int session, const void *msg, size_t sz);
using t_mctp_writeread_cb = luawrap::lua_function<
        std::tuple<gint32, std::optional<std::string_view>>(guint8, std::string_view, guint8, guint32),
            luawrap::ref_value>;

class l_callbacks {
    friend class register_ctrl_oob;

public:
    struct params {
        t_skynet_send skynet_send;
        uint8_t      *read_buf;
        uint8_t       read_len;
        int           result;
        int           wait_tid;
        int           handle;
        int           msg_tag;

        std::mutex              write_read_mutex;
        std::condition_variable condition;
        std::optional<t_mctp_writeread_cb> mctp_writeread_cb;

        params() noexcept
            : skynet_send(nullptr), read_buf(nullptr), read_len(0), result(0), wait_tid(0), handle(0), msg_tag(0)
        {
        }

        int  block_write_read(guint8 obj_index, const guint8 *pWritebuf, guint8 write_length, guint8 *pReadbuf,
                              guint8 read_length);
        void reply_write_read(int rsp, std::optional<std::string_view> rsp_data);
    };

public:
    explicit l_callbacks(lua_State *L);
    ~l_callbacks();

    static void register_to(lua_State *L, luawrap::stack_table &t);

private:
    static luawrap::n_ret new_callbacks(lua_State *L);
    template <typename T>
    void reset_callback(std::optional<T> &cb)
    {
        if (cb && cb->m_L == m_L) {
            cb.reset();
        }
    }

private:
    lua_State *m_L;

private:
    static gint32 mctp_writeread_cb(guint8 obj_index, guint32 request_length, const guint8 *request,
                                    guint32 *response_length, guint8 *response, guint32 timeout);
    static gint32 i2c_write_cb(guint8 obj_index, guint8 *pWritebuf, guint8 write_length);
    static gint32 i2c_writeread_cb(guint8 obj_index, guint8 *pWritebuf, guint8 write_length, guint8 *pReadbuf,
                                   guint8 read_length);
    static std::map<int, std::unique_ptr<params>> s_params_map;

    bool                         init_handle(int ctrl_id, int handle, int tag);
    void                         reply_write_read(int ctrl_id, int result, std::optional<std::string_view> data);
    int                          get_wait_tid(int ctrl_id) const
    {
        auto it = s_params_map.find(ctrl_id);
        if (it != s_params_map.end()) {
            return it->second->wait_tid;
        }
        return -1;
    }
    void set_mctp_writeread_cb(int ctrl_id, std::optional<t_mctp_writeread_cb> cb)
    {
        auto it = s_params_map.find(ctrl_id);
        if (it == s_params_map.end()) {
            // mctp没有初始化的地方
            std::unique_ptr<l_callbacks::params> s_params = std::make_unique<l_callbacks::params>();
            s_params_map.emplace(ctrl_id, std::move(s_params));
            it = s_params_map.find(ctrl_id);
            if (it == s_params_map.end()) {
                return;
            }
        }

        // 将方法cb打包成全局变量s_mctp_writeread_cb
        auto &s_params = *it->second;
        std::unique_lock<std::mutex> guard(s_params.write_read_mutex);
        s_params.mctp_writeread_cb = std::move(cb);
    }
};

}  // namespace sml

#endif