-- Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
-- 
-- this file licensed under the 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.
--

local class = require 'mc.class'
local log = require 'mc.logging'
local bs = require 'mc.bitstring'
local protocol = require 'libmgmt_protocol.protocol.protocol'
local ctx = require 'mc.context'

local nvme_mi_standard = class(protocol)

local IC<const> = '1'  -- Integrity Check

local request_params_template<const> = {
    message_type = true,
    command_slot_identifier = true,
    data = true
}

-- 校验参数字段合法性和取值合法性
function nvme_mi_standard:validate_request_params(req)
    for key in pairs(req) do
        if not self.request_params_template[key] then
            return false
        end
    end

    -- nvme-mi message当前必须传入，0为Control Primitive
    -- 其他为控制报文，1为NVME-MI Command，2为NVME Admin Command，4为PCIe Command，其余取值预留暂不支持
    if req.message_type and (req.message_type ~= 0 and req.message_type ~= 1 and
        req.message_type ~= 2 and req.message_type ~= 4) then
        log:debug('unable to construct request data with invalid nvme message type:%s',
            req.message_type or nil)
        return false
    end

    -- 启动自动暂停，控制报文可选0和1，其余报文根据协议限定为0，否则会返回错误(框架实现)
    -- 管理endpoint的buffer，控制报文可选0和1，其余报文根据协议限定为0，否则会返回错误(框架实现)
    -- 命令槽位必须传入，根据协议当前仅可取0或1
    if req.command_slot_identifier and (req.command_slot_identifier ~= 0 and
        req.command_slot_identifier ~= 1) then
        log:debug('unable to construct request data with invalid command slot identifier:%s',
            req.command_slot_identifier or nil)
        return false
    end

    return true
end

function nvme_mi_standard:construct_request_data(ctx, request)
    ctx.MsgType = tostring(request.message_type)
    ctx.CmdSlot = tostring(request.command_slot_identifier)
    ctx.IC = IC
    local rsp_ctx = { }
    return ctx, rsp_ctx, request.data or ''
end

function nvme_mi_standard:unpack_response_data(ctx, rsp_bin)
    if not rsp_bin then
        return nil
    end
    return rsp_bin
end

function nvme_mi_standard:send_request(request)
    local para_ctx = {}
    local req_ctx, rsp_ctx, request_bin = self:construct_request_data(para_ctx, request)
    local ok, rsp_data_bin = pcall(self.endpoint.Request, self.endpoint, ctx.get_context_or_default(),
        request_bin, 0, req_ctx, rsp_ctx)
    if not ok or not rsp_data_bin then
        log:debug('unable to send nvme request to endpoint: %s, error: %s',
            self.endpoint.TargetPhyAddr, rsp_data_bin)
        return nil
    end

    return self:unpack_response_data(para_ctx, rsp_data_bin)
end

function nvme_mi_standard:ctor(params)
    if not params or not params.endpoint then
        log:raise('unable to create nvme protocol with invalid params')
    end
    self.name = 'nvme_mi_standard'
    self.endpoint = params.endpoint
    self.request_params_template = request_params_template
end

return nvme_mi_standard
