-- 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 bs = require 'mc.bitstring'
local log = require 'mc.logging'
local ncsi_standard = require 'protocol_open.protocol.ncsi_standard'

local ncsi_huawei = class(ncsi_standard)
local nsci_oem_request_packet_type<const> = 0x50
local nsci_oem_response_packet_type<const> = 0xD0
local huawei_manufacture_id<const> = 0x07DB

local response_bs<const> = bs.new([[<<
    response_code:16/big,
    reason_code:16/big,
    manufacture_id:32/big,
    _:8,
    huawei_cmd_id:8,
    sub_cmd_id:8,
    data/string
>>]])

local request_bs<const> = bs.new([[<<
    manufacture_id:32/big,
    _:8,
    huawei_cmd_id:8,
    sub_cmd_id:8,
    extra_cmd:8
>>]])

local request_params_template<const> = {
    channel_id = true,
    package_id = true,
    huawei_cmd_id = true,
    sub_cmd_id = true,
    extra_cmd = true,
    data = true
}

local response_error_code<const> = {
    [0x1] = {
        [0x8002] = 'invalid OEM CMD head info',
        [0x8003] = 'OEM get info failed',
        [0x8004] = 'OEM error code over maximum',
        [0x8005] = 'OEM up get driver info failed',
        [0x8006] = 'OEM cable type not supported',
        [0x8007] = 'OEM cable type undefined',
        [0x8008] = 'OEM optical module not exist',
        [0x9000] = 'OEM get controller stats failed'
    },
    [0x3] = {[0x7FFFF] = 'unsupported command'}
}

function ncsi_huawei:construct_request_data(ctx, request)
    ctx.manufacture_id = huawei_manufacture_id
    ctx.huawei_cmd_id = request.huawei_cmd_id
    ctx.sub_cmd_id = request.sub_cmd_id
    ctx.extra_cmd = request.extra_cmd or 0

    local data = request_bs:pack({
        manufacture_id = ctx.manufacture_id,
        huawei_cmd_id = ctx.huawei_cmd_id,
        sub_cmd_id = ctx.sub_cmd_id,
        extra_cmd = ctx.extra_cmd
    })

    return ncsi_huawei.super.construct_request_data(self, ctx, {
        packet_type = nsci_oem_request_packet_type,
        expect_rsp_packet_type = nsci_oem_response_packet_type,
        channel_id = request.channel_id,
        package_id = request.package_id,
        data = data .. (request.data or '')
    })
end

function ncsi_huawei:unpack_response_data(ctx, rsp_bin)
    local rsp = response_bs:unpack(rsp_bin, true)

    if not rsp then
        log:debug('[protocol %s]: empty data received for ncsi_huawei response', self.name)
        return nil
    end

    -- 0x8008表示光模块不在位，需要返回应用层作特殊处理
    if rsp.reason_code == 0x8008 then
        return "ncsi_oem_not_present"
    end

    if rsp.response_code ~= 0 then
        local rsp_code = rsp.response_code
        local rea_code = rsp.reason_code
        if response_error_code[rsp_code] and response_error_code[rsp_code][rea_code] then
            rea_code = string.format('%s(%s)', rea_code, response_error_code[rsp_code][rea_code])
        end
        log:debug('[protocol %s]: unsuccessful request with response code: %s, reason code: %s',
            self.name, rsp_code, rea_code)
        return nil
    end

    if not rsp.manufacture_id or rsp.manufacture_id ~= huawei_manufacture_id then
        log:debug('[protocol %s]: invalid response data with wrong manufacture_id: %s', self.name,
            rsp.manufacture_id)
        return nil
    end

    if not rsp.huawei_cmd_id or rsp.huawei_cmd_id ~= ctx.huawei_cmd_id or not rsp.sub_cmd_id or
        rsp.sub_cmd_id ~= ctx.sub_cmd_id then
        log:debug(
            '[protocol %s]: invalid response data with cmd: huawei_cmd_id: %s, sub_cmd_id: %s',
            self.name, rsp.huawei_cmd_id, rsp.sub_cmd_id)
        return nil
    end

    return rsp.data
end

function ncsi_huawei:ctor()
    self.name = 'ncsi_huawei'
    self.request_params_template = request_params_template
end

return ncsi_huawei
