-- 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 log = require 'mc.logging'
local cjson = require 'cjson'
local enums = require 'ipmi.enums'
local m_error_custom = require 'messages.custom'
local context = require 'mc.context'

local m = {}


local RAW_VALID_LEN = 2
local IPMB_VALID_LEN = 3

local function check_field_valid(req_fields, valid_len)
    if #req_fields < valid_len then
        local ctx = context.get_context_or_default()
        log:operation(ctx:get_initiator(), 'ipmi_core', 'Send ipmi command failed')
        error(m_error_custom.IPMIRequestLengthInvalid())
    end

    local val
    for _, field in ipairs(req_fields) do
        val = tonumber(field)
        if not val or (val < 0x00 or val > 0xFF) then
            log:error("field [%s] is invalid", field)
            local ctx = context.get_context_or_default()
            log:operation(ctx:get_initiator(), 'ipmi_core', 'Send ipmi command failed')
            error(m_error_custom.IPMIInvalidFieldRequest())
        end
    end
end

local function parse_req_msg(req_msg, valid_len)
    local req_fields = {}
    for match in req_msg:gmatch('%S+') do
        table.insert(req_fields, match)
    end

    check_field_valid(req_fields, valid_len)

    return req_fields
end

local function create_req(req_fields)
    local req_table = {}
    table.insert(req_table, 0x20) -- DestAddr
    local netfn = tonumber(req_fields[1])
    table.insert(req_table, (netfn << 2 | 0x00) & 0xFF) -- NetFn/Lun
    table.insert(req_table, 0) -- ChkSum1
    table.insert(req_table, 0x20) -- SrcAddr
    table.insert(req_table, 0) -- SrcSeq/SrcLun
    table.insert(req_table, tonumber(req_fields[2])) -- Cmd
    for i = 3, #req_fields do -- Payload
        table.insert(req_table, tonumber(req_fields[i]))
    end
    table.insert(req_table, 0) -- end

    return req_table
end

local function create_ctx()
    local dbus_ctx = context.get_context_or_default()
    return cjson.encode({
        ChanType = enums.ChannelType.CT_LAN:value(),
        chan_num = enums.ChannelId.CT_LAN1:value(),
        Instance = 1,
        HostId = 1,
        dest_lun = 0,
        from = "CLI",
        client = {
            ip = '127.0.0.1'
        },
        session = {
            user = {
                name = dbus_ctx.UserName,
                role_priv = dbus_ctx.Privilege
            }
        }
    })
end

local function route_cmd(req, ctx_table)
    local ctx = context.get_context_or_default()
    local rsp = bus:call('bmc.kepler.ipmi_core', '/bmc/kepler/IpmiCore', 'bmc.kepler.IpmiCore',
        'Route', 'a{ss}ayay', ctx, req, ctx_table)

    local rsp_msg = ''
    for i = 1, #rsp do
        rsp_msg = rsp_msg .. string.format('%02X ', string.byte(rsp:sub(i, i)))
    end
    return rsp_msg
end

-- 发送raw命令
function m.process_ipmi_cmd(req_msg)
    local req_fields = parse_req_msg(req_msg, RAW_VALID_LEN)
    local netfn, cmd = req_fields[1], req_fields[2]

    local rsp_msg = route_cmd(create_req(req_fields), create_ctx())
    local oper_success = string.format('Send ipmi command (%s %s ...) successfully', netfn, cmd)
    local ctx = context.get_context_or_default()
    log:operation(ctx:get_initiator(), 'ipmi_core', oper_success)
    return rsp_msg
end

local ipmb_msg = {
    '0x30', -- NetFn
    '0x93', -- Cmd
    '0xdb', '0x07', '0x00', -- ManuId
    '0x7a', -- subCmd
    '0x01', -- TargetType
}
-- 桥接ipmb命令
function m.bridge_ipmb_cmd(req_msg)
    local req_fields = parse_req_msg(req_msg, IPMB_VALID_LEN)
    local addr, netfn, cmd = req_fields[1], req_fields[2], req_fields[3]
    table.insert(req_fields, 2, string.format('0x%02X', #req_fields - 1)) -- Length
    for i = #ipmb_msg, 1, -1 do
        table.insert(req_fields, 1, ipmb_msg[i])
    end

    local rsp_msg = route_cmd(create_req(req_fields), create_ctx())
    local oper_success = string.format('Send ipmi command (%s %s ...) to ipmb (%s) successfully', netfn, cmd, addr)
    local ctx = context.get_context_or_default()
    log:operation(ctx:get_initiator(), 'ipmi_core', oper_success)
    return rsp_msg
end

return m
