-- 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 mdb = require 'mc.mdb'
local def = require 'biz_topo.def'
local debounce = require 'mc.debounce.debounce'
local c_base = require 'biz_topo.class.base'
local log = require 'mc.logging'
local software_event_mgmt = require 'software_event_mgmt'
local class_mgnt = require 'mc.class_mgnt'

---@class BusinessConnector: Base @业务TOPO连接器
---@field public mds_obj table @MDS对象
---@field public ports Port[] @端口
local c_business_connector = class(c_base, nil, true)

function c_business_connector:is_pcie_biz_connector()
    -- RefMgmtConnector为table类型时说明引用了Connector对象
    if self.mds_obj.Direction == 'Downstream' and self.mds_obj.RefPCIeAddrInfo ~= '' then
        return true
    end
    return false
end

function c_business_connector:is_src_biz_connector()
    if self.mds_obj.Direction == 'Downstream' and self.mds_obj.Ports and next(self.mds_obj.Ports) then
        return true
    end
    return false
end

-- 判断是IEU、SEU上行连接器
function c_business_connector:is_pcie_up_biz_connector()
    if self.mds_obj.Direction == 'Upstream' and self.mds_obj.Ports and next(self.mds_obj.Ports) then
        return true
    end
    return false
end

local function port_new(Name, ID, Offset, Width)
    return { Name = Name, ID = ID, Offset = Offset, Width = Width }
end

function c_business_connector:init()
    if self.mds_obj.Ports and #self.mds_obj.Ports ~= 0 and
        #self.mds_obj.Ports[1] ~= 0 then
        for i, port in ipairs(self.mds_obj.Ports) do
            self.mds_obj.Ports[i] = port_new(table.unpack(port))
        end
    end
    if self.mds_obj.UpstreamResources and #self.mds_obj.UpstreamResources ~= 0 and
        #self.mds_obj.UpstreamResources[1] ~= 0 then
        for i, res in ipairs(self.mds_obj.UpstreamResources) do
            self.mds_obj.UpstreamResources[i] = port_new(table.unpack(res))
        end
    end

    -- 初始化 ports
    if self.mds_obj.Ports and #self.mds_obj.Ports ~= 0 then
        for i, p in ipairs(self.mds_obj.Ports) do
            ---@class Port @业务端口
            ---@field public zone string
            ---@field public name string
            ---@field public id number
            ---@field public target_unit_uid string @所连接组件的uid
            ---@field public target_unit_index number @所连接组件的index
            ---@field public target_unit_port_id number @所连接组件的port_id
            ---@field public target_unit_slot_silk string @所连接组件所在的槽位信息
            ---@field public status number @TOPO检测状态
            ---@field public is_discovered boolean @拓扑发现是否包含该端口信息
            self.ports[i] = {
                zone = string.sub(p.Name, 1, 1),
                name = p.Name,
                id = p.ID,
                target_unit_uid = '',
                target_unit_index = 0,
                target_unit_port_id = 0,
                target_unit_slot_silk = '',
                status = 0,
                is_discovered = false
            }
        end
    end

    local monitor_status_debounce = {}
    for i, _ in pairs(def.PROP_PORT_LINK_INFO) do
        monitor_status_debounce[def.PROP_PORT_LINK_INFO[i]] = debounce['Cont'].new(def.DBD_CONT.CONT_NUM, 255)
    end
    self.monitor_status = monitor_status_debounce
end

function c_business_connector:on_add_object_complete(bus)
    if self.ref_mgmt_connector then
        return
    end
    local mgmt_conn_obj = self.mds_obj.RefMgmtConnector
    if not mgmt_conn_obj or not mgmt_conn_obj.path then
        return
    end
    -- 注：对引用对象设置属性值不会生效，这里需要通过mdb重新获取资源树对象
    local ok
    ok, mgmt_conn_obj = pcall(mdb.get_object, bus, mgmt_conn_obj.path, 'bmc.kepler.Connector')
    if not ok or not mgmt_conn_obj then
        return
    end

    self.ref_mgmt_connector = mgmt_conn_obj
    if not self.mds_obj.RefMgmtConnectorTianChi or not self.mds_obj.RefMgmtConnectorTianChi.path then
        return
    end
    mgmt_conn_obj = self.mds_obj.RefMgmtConnectorTianChi
    ok, mgmt_conn_obj = pcall(mdb.get_object, bus, mgmt_conn_obj.path, 'bmc.kepler.Connector')
    if ok and mgmt_conn_obj then
        self.ref_mgmt_connector_tianchi = mgmt_conn_obj
    end
end

function c_business_connector:on_delete_object()
    local sw_event_mgmt = software_event_mgmt.get_instance()
    if sw_event_mgmt and sw_event_mgmt.conn_pair_list then
        sw_event_mgmt.conn_pair_list[self.mds_obj.ObjectName] = nil
    end
end

function c_business_connector:ctor(mds_obj, position, complete_signal, bus)
    self.default_interface = 'bmc.kepler.Systems.UnitConfigError'
    self.mds_obj = mds_obj
    self.position = position
    self.ports = {}
    self.bcu_index = mds_obj.BCUIndex
    self.die_idxs = {}  --EthDevice使用
    self.bus_nums = {}  --EthDevice使用
    self.c_idxs = {}    --EthDevice使用
    self.monitor_update_event_task = false
    complete_signal:on(function(connector_position)
        if connector_position ~= self.position then
            return
        end
        self:on_add_object_complete(bus)

        if self.monitor_update_event_task then
            return
        end
        self.monitor_update_event_task = true

        if self.ref_mgmt_connector_tianchi and self.mds_obj.RefPCIeAddrInfo ~= '' then
            -- 仅支持UB接口卡配置告警
            if self.ref_mgmt_connector_tianchi.Type ~= 'UBInterfaceCard' then
                log:info('tc_conn type (%s) not supported', self.ref_mgmt_connector_tianchi.Type)
                return
            end
            -- 对引用对象设置属性值不生效，需要重新获取PCIeAddrInfo对象
            local ref_addr_info_obj = class_mgnt('PcieAddrInfo')[self.mds_obj.RefPCIeAddrInfo]
            if not ref_addr_info_obj then
                log:notice('update tc_connector_csr_failure failed, cannot match ref_addr_info_obj %s',
                    self.mds_obj.RefPCIeAddrInfo)
                return
            end

            local sw_event_mgmt = software_event_mgmt.get_instance()
            if not sw_event_mgmt or not sw_event_mgmt.conn_pair_list then
                return
            end
            sw_event_mgmt.conn_pair_list[self.mds_obj.ObjectName] = {
                tc_conn = self.ref_mgmt_connector_tianchi,
                conn = self.ref_mgmt_connector,
                pai = ref_addr_info_obj
            }
            sw_event_mgmt.update_event_task_list['tc_connector_csr_failure'](
                sw_event_mgmt, self.ref_mgmt_connector_tianchi, self.ref_mgmt_connector, ref_addr_info_obj)
        end
    end)
end

return c_business_connector
