-- 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 class = require 'mc.class'
local c_base = require 'biz_topo.class.base'
local cmn = require 'common'
local log = require 'mc.logging'

---@class BusinessConnector: Base @业务TOPO连接器
---@field public mds_obj table @MDS对象
---@field public configs BDFConfiguration[] @端口
local c_bdf_config = class(c_base)

function c_bdf_config:config_new(PortId, ControllerIndex, Segment, Bus, Device, Function, Type)
    return { PortId = PortId, ControllerIndex = ControllerIndex, Segment = Segment,
        Bus = Bus, Device = Device, Function = Function, Type = Type }
end

local function config_tbl_new(PortId, ControllerIndex, Segment, Bus, Device, Function, Type)
    return { PortId or 0, ControllerIndex or 255, Segment or 0, Bus or 0, Device or 0, Function or 0 , Type or 0 }
end

function c_bdf_config:build_bdf_cfgs()
    -- 初始化 configs
    local cfg
    if self.mds_obj.BDFConfigs and #self.mds_obj.BDFConfigs ~= 0 then
        for i, c in ipairs(self.mds_obj.BDFConfigs) do
            self.mds_obj.BDFConfigs[i] = config_tbl_new(table.unpack(c, 1, 7))
            ---@class BDFConfiguration @业务端口
            cfg = self:config_new(table.unpack(c, 1, 7))
            self.configs[i] = {
                socket_id = self.mds_obj.SocketId,
                type = cfg.Type,
                port_id = cfg.PortId,
                controller_index = cfg.ControllerIndex or 255,
                segment = cfg.Segment,
                bus = cfg.Bus,
                device = cfg.Device,
                func = cfg.Function
            }
        end
    end
end

function c_bdf_config:check_config(configs)
    local tbl = {}
    local config
    for _, cfg in pairs(configs) do
        config = self:config_new(table.unpack(cfg, 1, 7))
        if not config.Type or not config.ControllerIndex or not config.Bus then
            log:notice("[BDFconfig] prop not exist")
            return false
        end
        if not tbl[config.Type] then
            tbl[config.Type] = {}
        end
        if tbl[config.Type][config.ControllerIndex] and tbl[config.Type][config.ControllerIndex] ~= config.Bus then
            log:notice("[BDFconfig] Type %s ControllerIndex %s already exist Bus %s, cfg Bus %s", config.Type,
            config.ControllerIndex, tbl[config.Type][config.ControllerIndex], config.Bus)
            return false
        else
            tbl[config.Type][config.ControllerIndex] = config.Bus
        end
    end
    return true
end

function c_bdf_config:check_db(db)
    local socket_id = self.mds_obj.SocketId
    local cfg_length = self.mds_obj.ConfigLength
    local retry = 120
    local v
    while retry > 0 do
        v = db:select(db.BDFConfig):where(db.BDFConfig.SocketId:eq(socket_id)):first()
        if v then
            -- 检查持久化数据库中表长度和配置的表长是否一致
            if cfg_length ~= 255 and #v.BDFConfigs ~= cfg_length then
                log:notice("[BDFconfig] database check failed, cfg = %s, csr = %s", #v.BDFConfigs, cfg_length)
                db:delete(db.BDFConfig):where(db.BDFConfig.SocketId:eq(socket_id)):exec()
                return
            end
            -- 检测同一个ControllerType, ControllerIndex下是否有重复的Bus配置
            if not self:check_config(v.BDFConfigs) then
                log:notice("[BDFconfig] database check_config failed")
                db:delete(db.BDFConfig):where(db.BDFConfig.SocketId:eq(socket_id)):exec()
                return
            end
            log:notice("[BDFconfig] database check ok")
            return
        end
        retry = retry - 1
        cmn.skynet.sleep(100)
    end
end


function c_bdf_config:init()
    -- 将数组重新转成结构体
    if self.mds_obj.BDFConfigs and #self.mds_obj.BDFConfigs ~= 0 and
        #self.mds_obj.BDFConfigs[1] ~= 0 then
        for i, config in ipairs(self.mds_obj.BDFConfigs) do
            self.mds_obj.BDFConfigs[i] = config_tbl_new(table.unpack(config, 1, 7))
        end
    end
end

function c_bdf_config:ctor(mds_obj, position)
    self.mds_obj = mds_obj
    self.position = position
    self.configs = {}
end

return c_bdf_config
