-- 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.

local ipmi = require 'ipmi'
local bs = require 'mc.bitstring'
local log = require 'mc.logging'
local enums = require 'ipmi.enums'
local cjson = require 'cjson'

local channel_type = enums.ChannelType
local comp_code = ipmi.types.Cc

local imu_cmd = {}

local LOCAL_DIRECTION<const> = 0
local REMOTE_DIRECTION<const> = 1
local CMD_STATUS_FAILED<const> = 1  -- IMP不支持或未准备好
local CMD_STATUS_SUCCESS<const> = 0 -- 获取成功

-- 子命令字是0x70
local ub_port_info_req = bs.new([[<<
    0xDB0700:3/unit:8,
    0x70,
    cpu_id:1/unit:8,
    controller_id:1/unit:8,
    direction:1/unit:8,
    port_id:1/unit:8
>>]])

local ub_port_rsp_pre = bs.new([[<<
    0xDB0700:3/unit:8,
    cmd_status:1/unit:8,
    tail/binary
>>]])

local ub_port_local_rsp = bs.new([[<<
    0xDB0700:3/unit:8,
    cmd_status:1/unit:8,
    char1:1/unit:8,
    char2:1/unit:8,
    char3:1/unit:8,
    char4:1/unit:8,
    char5:1/unit:8,
    char6:1/unit:8,
    char7:1/unit:8,
    char8:1/unit:8,
    char9:1/unit:8,
    char10:1/unit:8,
    char11:1/unit:8,
    char12:1/unit:8,
    char13:1/unit:8,
    char14:1/unit:8,
    char15:1/unit:8,
    char16:1/unit:8,
    port_num:1/unit:8,
    tail/binary
>>]])

local local_rsp_format = bs.new([[<<
    link_status:1/unit:8,
    tail/binary
>>]])

local ub_port_remote_rsp = bs.new([[<<
    0xDB0700:3/unit:8,
    cmd_status:1/unit:8,
    port_num:1/unit:8,
    resv:3/unit:8,
    tail/binary
>>]])

local remote_rsp_format = bs.new([[<<
    port_id:1/unit:8,
    link_status:1/unit:8,
    resv:2/unit:8,
    char1:1/unit:8,
    char2:1/unit:8,
    char3:1/unit:8,
    char4:1/unit:8,
    char5:1/unit:8,
    char6:1/unit:8,
    char7:1/unit:8,
    char8:1/unit:8,
    char9:1/unit:8,
    char10:1/unit:8,
    char11:1/unit:8,
    char12:1/unit:8,
    char13:1/unit:8,
    char14:1/unit:8,
    char15:1/unit:8,
    char16:1/unit:8,
    tail/binary
>>]])

-- 解析ipmi返回消息
local function parse_ub_port_info_local(payload)
    local rsp = ub_port_rsp_pre:unpack(payload)
    if rsp.cmd_status == CMD_STATUS_FAILED then
        log:info('parse_ub_port_info_local failed, cmd_status=%s.', rsp.cmd_status)
        return
    end

    -- 获取成功，包含本端GUID等信息
    rsp = ub_port_local_rsp:unpack(payload)
    local port_num = rsp.port_num
    local tail = rsp.tail
    local link_status_list = {}
    local read_loop, port_status

    local hex_str = ""
    for i = 1, 16 do
        local char_index = "char" .. i
        hex_str = hex_str .. string.format("%02X", rsp[char_index])
    end

    for i = 1, port_num do
        read_loop = local_rsp_format:unpack(tail)
        port_status, tail = read_loop.link_status, read_loop.tail
        link_status_list[#link_status_list + 1] = port_status  -- 0表示建链失败，1表示建链成功
    end
    return {
        direction = LOCAL_DIRECTION,
        guid = hex_str,
        link_status_list = link_status_list
    }
end

local function parse_ub_port_info_remote(payload)
    local rsp = ub_port_remote_rsp:unpack(payload)
    if rsp.cmd_status == CMD_STATUS_FAILED then
        log:info('parse_ub_port_info_remote failed, cmd_status=%s.', rsp.cmd_status)
        return
    end
    local port_num = rsp.port_num
    local tail = rsp.tail
    local remote_link_status_list = {}
    local read_loop
    for i = 1, port_num do
        read_loop = remote_rsp_format:unpack(tail)
        tail = read_loop.tail

        local hex_str = ""
        for i = 1, 16 do
            local char_index = "char" .. i
            hex_str = hex_str .. string.format("%02X", read_loop[char_index])
        end

        remote_link_status_list[#remote_link_status_list + 1] = {
            remote_port_id = read_loop.port_id,
            remote_port_status = read_loop.link_status,
            remote_port_guid = hex_str
        }
    end
    return {
        direction = REMOTE_DIRECTION,
        remote_link_status_list = remote_link_status_list
    }
end

local function parse_ub_port_info(payload, direction)
    local rsp = ub_port_rsp_pre:unpack(payload)
    if rsp.cmd_status == CMD_STATUS_FAILED then
        log:info('parse_ub_port_info_local failed, cmd_status=%s.', rsp.cmd_status)
        return
    end

    local parse_fun = {
        [LOCAL_DIRECTION] = parse_ub_port_info_local,
        [REMOTE_DIRECTION] = parse_ub_port_info_remote
    }
    return parse_fun[direction](payload)
end

-- 查询UB端口建链信息
function imu_cmd.get_ub_port_info(bus, cpu_id, controller_id, direction, port_id)
    if not bus then
        log:error('get_ub_port_info failed, bus error.')
        return nil
    end
    local req_data = ub_port_info_req:pack({
        cpu_id = cpu_id or 0,
        controller_id = controller_id or 0,
        direction = direction or 0,
        port_id = port_id or 0
    })
    local result, payload = ipmi.request(bus, {channel_type.CT_ME:value(), 0},
        {DestNetFn = 0x30, Cmd = 0x98, Payload = req_data})
    if result == comp_code.Success then
        local info_arr = parse_ub_port_info(payload, direction)
        log:debug('get_ub_port_info, info_arr=%s ', cjson.encode(info_arr))
        return info_arr
    end
    log:debug('get_ub_port_info failed, error: %s, cpu_id=%s, controller_id=%s, direction=%s, port_id=%s', result,
        cpu_id, controller_id, direction, port_id)
end

return imu_cmd
