-- 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 ctx = require 'mc.context'
local skynet = require 'skynet'
local log = require 'mc.logging'
local initiator = require 'mc.initiator'
local common_utils = require 'common_tools.common_utils'
local m_error_base = require 'messages.base'
local CHASSIS_INTF = 'bmc.kepler.Chassis'

local chassis_manager = {}
chassis_manager.__index = chassis_manager

-- 子组件数量统计，电源组件需添加上窄电源和电源转换器的连接器数量
local device_info_with_subcomponent = {
    ['PowerConverter'] = 'Psu',
    ['PsuBoard'] = 'Psu'
}

function chassis_manager.new_for_csr(obj, position)
    obj['bmc.kepler.Chassis']:enable_shm_prop_cache('DeviceSpecication', false)
    return setmetatable({chassis_object = obj, device_count = {}, position = position, connector_num = {},
        connector_map = {}, device_map = {}}, chassis_manager)
end

function chassis_manager:get_intrusion_ac_on()
    return self.chassis_object.IntrusionACOn
end

function chassis_manager:get_intrusion_ac_off()
    return self.chassis_object.IntrusionACOff
end

function chassis_manager:get_intrusion_ac_on_clear()
    return self.chassis_object.IntrusionACOnClear
end

function chassis_manager:get_intrusion_ac_off_clear()
    return self.chassis_object.IntrusionACOffClear
end

function chassis_manager:set_intrusion_ac_on_clear(intrusion_clear_code)
    self.chassis_object.IntrusionACOnClear = intrusion_clear_code
end

function chassis_manager:set_intrusion_ac_off_clear(intrusion_clear_code)
    self.chassis_object.IntrusionACOffClear = intrusion_clear_code
end

function chassis_manager:get_cover_status()
    return self.chassis_object.CoverStatus
end

function chassis_manager:get_intrusion_flag()
    return self.chassis_object.IntrusionFlag
end

function chassis_manager:set_intrusion_flag(intrusion_flag_code)
    -- 防止信号变更过快，sleep让出协程
    skynet.sleep(1)
    self.chassis_object.IntrusionFlag = intrusion_flag_code
end

function chassis_manager:get_last_intrusion_ac_on()
    return self.chassis_object.LastIntrusionACOn
end

function chassis_manager:set_last_intrusion_ac_on(intrusion_ac_on_code)
    self.chassis_object.LastIntrusionACOn = intrusion_ac_on_code
end

function chassis_manager:set_prop(prop_name, value)
    self.chassis_object[prop_name] = value
end

function chassis_manager:get_prop(prop_name)
    return self.chassis_object[prop_name]
end

function chassis_manager:get_device_max_count(device_name)
    for _, device_info in pairs(self.chassis_object.DeviceSpecication) do
        if device_info[1] == device_name then
            return device_info[2]
        end
    end
end

function chassis_manager:set_device_max_count_and_slot(device_name, max_num)
    if not self.chassis_object.DeviceSpecication then
        log:error("[chassis]set_device_max_count_and_slot: set % max num failed", device_name)
        return
    end
 
    for _, device_info in pairs(self.chassis_object.DeviceSpecication) do
        if device_info[1] == device_name then
            device_info[2] = max_num
            device_info[3] = {}
            if self.slot_map and self.slot_map[device_name] then
                device_info[3] = self.slot_map[device_name]
            end
            return
        end
    end
 
    local device_info = {device_name, max_num, {}}
    if self.slot_map and self.slot_map[device_name] then
        device_info = {device_name, max_num, self.slot_map[device_name]}
    end
    table.insert(self.chassis_object.DeviceSpecication, device_info)
end

function chassis_manager:get_device_num(device_name)
    return self.device_map[device_name] or 0
end

function chassis_manager:get_connector_num(connector_name)
    return self.connector_map[connector_name] or 0
end

function chassis_manager:update_device_num(device_name, device_num)
    self.device_map[device_name] = device_num
end

function chassis_manager:update_connector_num(connector_name, connector_num)
    self.connector_map[connector_name] = connector_num
end

function chassis_manager:init_connector()
    self.connector_num = {}
end

function chassis_manager:init_slots()
    self.slot_map = {}
end

function chassis_manager:update_connector_max_count(device_name)
    local max_num = self.connector_num[device_name] or 0
    max_num = max_num + 1
    self.connector_num[device_name] = max_num
end

function chassis_manager:update_slot(device_name, slot)
    local slots = self.slot_map[device_name] or {}
    table.insert(slots, slot)
    self.slot_map[device_name] = slots
end

-- 子组件连接器数量累计到对应组件中
function chassis_manager:update_connector_with_subcomponent()
    for subcomponent, component in pairs(device_info_with_subcomponent) do
        if self.connector_num[component] == nil then
            self.connector_num[component] = 0
        end

        if self.connector_num[subcomponent] then
            self.connector_num[component] = self.connector_num[component] + self.connector_num[subcomponent]
        end
    end
end

function chassis_manager:update_connector()
    for connector_name, connector_num in pairs(self.connector_num) do
        local device_num = self:get_device_num(connector_name)
        self:update_connector_num(connector_name, connector_num)
        local real_max_num = (device_num > connector_num) and device_num or connector_num
        log:debug('[chassis]update connectors, device_name:%s, connector_num:%s, device_num:%s, real_num:%s',
            connector_name, connector_num, device_num, real_max_num)
        self:set_device_max_count_and_slot(connector_name, real_max_num)
    end
end

function chassis_manager:update_device(device_name, device_num)
    local connector_num = self:get_connector_num(device_name)
    self:update_device_num(device_name, device_num)
    log:notice('name = %s, connector_num = %s, device_num = %s', device_name, connector_num, device_num)
    local real_max_num = (connector_num > device_num) and connector_num or device_num
    log:debug('[chassis]update devices, device_name:%s, connector_num:%s, device_num:%s, real_num:%s',
        device_name, connector_num, device_num, real_max_num)
    self:set_device_max_count_and_slot(device_name, real_max_num)
end

function chassis_manager:property_change()
    local location
    self.chassis_object[CHASSIS_INTF].property_before_change:on(function(name, value, sender)
        if sender == nil then
            return true
        end
        if name ~= 'Location' then
            return true
        end
        local m_initiator = initiator.new('N/A', 'N/A', 'localhost')
        local context = ctx.get_context()
        local _initiator = context and context:get_initiator() or m_initiator
        if not common_utils.validate_byte(name, value) then
            error(m_error_base.PropertyValueFormatError(tostring(value), "%Location"))
        end
        location = common_utils.trim(value)
        log:operation(_initiator, 'Chassis', 'Set chassis location to (%s) successfully', location)
        return true
    end)
end

return chassis_manager