-- 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 common = require 'common'
local log = require 'mc.logging'

local fru_id_mgmt = {}
local current_position_fru_id_mapping = {}
local previous_position_fru_id_mapping = {}
local fru_id_used = { false }
local queue = require 'skynet.queue'
local cs = queue()

-- 如果都分配完了，使用最大值63
local function get_fru_id()
    for index, value in ipairs(fru_id_used) do
        if not value then
            fru_id_used[index] = true
            return index
        end
    end
    return common.FRU_ID_MAX_AUTO_ALLOC
end

function fru_id_mgmt.allot_fruid(obj, position, db)
    if obj.FruId and (obj.FruId > common.FRU_ID_MAX_AUTO_ALLOC or obj.FruId == 0) then
        return
    end
    cs(function ()
        -- 如果之前分配过，优先使用
        if previous_position_fru_id_mapping[position] then
            current_position_fru_id_mapping[position] = previous_position_fru_id_mapping[position]
        end
        -- 如果未分配过，分配一个fru_id
        if not current_position_fru_id_mapping[position] then
            current_position_fru_id_mapping[position] = get_fru_id()
        end
        -- 将对应position的id分配给对应fru
        obj.FruId = current_position_fru_id_mapping[position]
        -- 持久化当前参与分配的fruid
        local ok, ret = pcall(function ()
            local fru_id_obj = db.PositionFruIdMapping({ Id = current_position_fru_id_mapping[position] })
            fru_id_obj.Position = position
            fru_id_obj:save()
        end)
        if not ok then
            log:error('PositionFruIdMapping save fru(%d) failed, ret(%s)', obj.FruId, ret)
        end
    end)
end

function fru_id_mgmt.free_fru_id(position, db)
    local fru_id = current_position_fru_id_mapping[position]
    if fru_id then
        fru_id_used[fru_id] = false
        local fru_id_obj = db.PositionFruIdMapping({ Id = fru_id })
        fru_id_obj:delete()
        current_position_fru_id_mapping[position] = nil
        previous_position_fru_id_mapping[position] = nil
    end
end

function fru_id_mgmt.init_fru_id(db)
    for i = 1, common.FRU_ID_MAX_AUTO_ALLOC, 1 do
        fru_id_used[i] = false
    end
    local frus = db:select(db.PositionFruIdMapping):all()
    for _, fru in ipairs(frus) do
        previous_position_fru_id_mapping[fru.Position] = fru.Id
        fru_id_used[fru.Id] = true
    end
    db:delete(db.PositionFruIdMapping):exec()
end

-- PCB版本, 根据PcbId的值进行计算得出, PcbId为1时, 显示.A , 为2时显示.B, 依次类推
local function set_pcbversion(object)
    local pcb_id = object.PcbId
    -- 字母z的ASCII编码比字母A大58
    if pcb_id >= 1 and pcb_id <= 58 then
        -- 字母A的ASCII编码为65
        object.PcbVersion = '.' .. string.char(pcb_id + 65 - 1)
    end
    if object.PcbVersion == '' then
        log:warn('Fru(%d) pcb id(%s) out of range', object.FruId, pcb_id)
        object.PcbVersion = '.A'
    end
end

function fru_id_mgmt.update_pcbversion(object)
    -- 初始化执行一次
    set_pcbversion(object)

    -- 监听PcbId变化
    object.property_changed:on(function (name, value, sender)
        if name == 'PcbId' then
            set_pcbversion(object)
            log:notice('update fru(%d) pcb version to (%s)', object.FruId, object.PcbVersion)
        end
    end)
end

return fru_id_mgmt
