-- 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 card_init_id = {}

local BUSINESSPORT_CARD_TYPE_VIR<const> = 6
local BUSINESSPORT_CARD_TYPE_TEAM<const> = 7
local BUSINESSPORT_CARD_TYPE_BRIDGE<const> = 8

local t_resource = {
    [0] = 'CPU1',
    [1] = 'CPU2',
    [2] = 'CPU3',
    [3] = 'CPU4',
    [251] = 'CPU1,CPU2,CPU3,CPU4',
    [253] = 'CPU1,CPU2',
    [254] = 'PCH',
    [255] = 'PCIeSwitch'
}

local function is_virtual_port(type)
    if type == BUSINESSPORT_CARD_TYPE_VIR or
       type == BUSINESSPORT_CARD_TYPE_BRIDGE or
       type == BUSINESSPORT_CARD_TYPE_TEAM then
        return true
    end
    return false
end

local function param_validator(device_locator)
    if device_locator == '' then
        return false
    end
    if  device_locator:find('%$') then
        return false
    end
    return true
end

local function set_resource_id_and_node_id(orm_obj)
    local type = orm_obj.Type
    local device_locator = orm_obj.DeviceLocator
    local position = orm_obj.Position
    if type == 3 then
        orm_obj.ID = string.gsub(device_locator, '%s+', '')
        orm_obj.NodeId = string.gsub(device_locator, '%s+', '')
        return
    end

    orm_obj.ID = string.gsub(position, '%s+', '') .. string.gsub(device_locator, '%s+', '')
    orm_obj.NodeId = string.gsub(position, '%s+', '') .. string.gsub(device_locator, '%s+', '')
end

local function update_root_bdf(orm_obj)
    log:notice('update root bdf, name:%s, BDF:%s:%s.%s, DeviceBDF:%s:%s.%s, type:%s',
        orm_obj.NodeId, orm_obj.Bus, orm_obj.Device, orm_obj.Function,
        orm_obj.DevBus, orm_obj.DevDevice, orm_obj.DevFunction, orm_obj.Type)
    local ok, val = pcall(string.format, '0000:%02x:%02x.%01x',
        orm_obj.Bus, orm_obj.Device, orm_obj.Function)
    if ok then
        orm_obj.RootBDF = val
    end
end

local function update_associated_resource(orm_obj)
    orm_obj.AssociatedResource = t_resource[orm_obj.SocketId]
end

local function listen_update_resource_tree_property(orm_obj)
    orm_obj:listen('SocketId', function(_, value)
        local resource = t_resource[value]
        if not resource then
            log:error('Invalid SocketId:%s', value)
            resource = 'CPU1'
        end
        orm_obj.AssociatedResource = resource
    end)
    orm_obj:listen('Bus', function(_, _)
        update_root_bdf(orm_obj)
    end)
    orm_obj:listen('Device', function(_, _)
        update_root_bdf(orm_obj)
    end)
    orm_obj:listen('Function', function(_, _)
        update_root_bdf(orm_obj)
    end)
end

local function properties_synchronizer(orm_obj)
    update_root_bdf(orm_obj)
    update_associated_resource(orm_obj)

    orm_obj:listen('DeviceLocator', function(_, value)
        if not param_validator(value) then
            return
        end
        set_resource_id_and_node_id(orm_obj)
    end)

    listen_update_resource_tree_property(orm_obj)
end

function card_init_id.init_id(orm_obj)
    -- bridge 不通过devicelocator生成ID信息
    if is_virtual_port(orm_obj.Type) then
        return
    end
    orm_obj.BoardIDHex = string.format('0x%04x', orm_obj.BoardID)

    local retry_times = 0
    while not param_validator(orm_obj.DeviceLocator) and retry_times < 10 do
        retry_times = retry_times + 1
        orm_obj.tasks:sleep_ms(5000)
    end
    set_resource_id_and_node_id(orm_obj)

    orm_obj:register_mdb_objects()
    properties_synchronizer(orm_obj)
end

return card_init_id