-- 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 json = require 'cjson'
local log = require 'mc.logging'
local ncsi_core = require 'ncsi.ncsi_core'

local NCSI_PACKAGE0 <const> = 0

local ENABLED <const> = 1

local NETWORK_MSG_SET_NCSI_RX_CHANNEL_STATE <const> = 13;

local IPMI_LOM_NCSI_TYPE <const> = 0
local IPMI_PCIE_NCSI_TYPE <const> = 1
local IPMI_MANAGEMENT_TYPE <const> = 2
local IPMI_LOM2_NCSI_TYPE <const> = 3
local IPMI_OCP_NCSI_TYPE <const> = 4
local IPMI_OCP2_NCSI_TYPE <const> = 5

local IPMI_CARD_TYPE_STR <const> = {
    [IPMI_LOM_NCSI_TYPE] = 'Lom',
    [IPMI_PCIE_NCSI_TYPE] = 'ExternalPCIe',
    [IPMI_MANAGEMENT_TYPE] = 'Dedicated',
    [IPMI_LOM2_NCSI_TYPE] = 'Lom2',
    [IPMI_OCP_NCSI_TYPE] = 'Ocp',
    [IPMI_OCP2_NCSI_TYPE] = 'Ocp2'
}

local function check_used_port(used_port, port_obj)
    return used_port.type == port_obj.type and used_port.port_num == port_obj.port_num
end

local function disable_ncsi_rx_channel(port_obj)
    -- 目前V3的eth_num默认都是0
    local eth_num = 0
    local eth_name = 'eth' .. eth_num
    local package_id = NCSI_PACKAGE0
    if port_obj.type == 'Ocp' or port_obj.type == 'Ocp2' then
        package_id = port_obj.package_id
    end
    ncsi_core.ncsi_disable_channel(package_id, port_obj.channel_id, eth_name)
    ncsi_core.ncsi_enable_brdcast_filter(package_id, port_obj.channel_id, eth_name)
    ncsi_core.ncsi_enable_multi_filter(package_id, port_obj.channel_id, eth_name)
end

local function disable_all_port_ncsi_rx_channel(db_ports, used_port)
    if type(db_ports) ~= 'table' then
        log:error('Get BusinessPort object list failed.')
        return false
    end

    for _, port_obj in pairs(db_ports) do
        -- json传输的数据[null]未被正常解析时，类型为userdate，而不是nil
        if type(port_obj) == 'table' and not check_used_port(used_port, port_obj) then
            disable_ncsi_rx_channel(port_obj)
        end
    end

    return true
end

-- 使能对应端口的RX
local function enable_ncsi_rx_channel_for_one_port(network_msg, port_num, tx_flag)
    local match_package_id = NCSI_PACKAGE0
    for _, db_port in pairs(network_msg.db_ports) do
        if db_port.type == IPMI_CARD_TYPE_STR[network_msg.ipmi_card_type] and
            db_port.port_num == port_num then
            match_package_id = db_port.package_id
        end
    end

    -- 目前V3的eth_num默认都是0
    local eth_num = 0
    local eth_name = 'eth' .. eth_num
    local package_id = NCSI_PACKAGE0
    if network_msg.ipmi_card_type == IPMI_OCP_NCSI_TYPE or network_msg.ipmi_card_type == IPMI_OCP2_NCSI_TYPE then
        package_id = match_package_id
    end

    local channel_id = network_msg.db_ports[port_num].channel_id
    ncsi_core.ncsi_enable_channel(package_id, channel_id, eth_name)
    if not tx_flag then
        ncsi_core.ncsi_disable_channelTX(package_id, channel_id, eth_name)
    end
    ncsi_core.ncsi_disable_brdcast_filter(package_id, channel_id, eth_name)
    ncsi_core.ncsi_disable_multi_filter(package_id, channel_id, eth_name)

    return true
end

local function rmcp_set_ncsi_rx_channel_state(network_msg)
    if not disable_all_port_ncsi_rx_channel(network_msg.db_ports, network_msg.used_port) then
        return false
    end

    if network_msg.ncsi_rx_channel_state ~= ENABLED then
        return true
    end

    for _, port_num in pairs(network_msg.ipmi_ports) do
        if port_num == 0xff then
            goto continue
        end

        -- ipmi请求的端口号需要加一与属性序号对应
        if not enable_ncsi_rx_channel_for_one_port(network_msg, port_num + 1,
                check_used_port(network_msg.used_port,
                    { type = IPMI_CARD_TYPE_STR[network_msg.ipmi_card_type], port_num = port_num + 1 })) then
            return false
        end
        ::continue::
    end
end

local func_map = {
    [NETWORK_MSG_SET_NCSI_RX_CHANNEL_STATE] = rmcp_set_ncsi_rx_channel_state
}

return function(worker)
    log:notice('ncsi set task start running!')
    local msg, network_msg, ok, rsp
    while worker:is_running() do
        msg = worker:recv()
        network_msg = json.decode(msg)
        if not func_map[network_msg.cmd] then
            log:error('receive msg type[%s] error', network_msg.cmd)
        else
            ok, rsp = pcall(func_map[network_msg.cmd], network_msg)
            if not ok then
                log:error('set task exec[%s] error: %s', network_msg.cmd, rsp)
            end
        end
    end
end
