-- 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.

-- Description: IPMI Entity management.
local log = require 'mc.logging'
local entity_instance = require 'entity.entity_instance'
local utils = require 'sensor_utils'

local entity_management = {}
entity_management.__index = entity_management

function entity_management.new(entity_sigs, sensor_sigs)
    return setmetatable({
        entities = {},
        entity_sigs = entity_sigs,
        sensor_sigs = sensor_sigs
    }, entity_management)
end

function entity_management:initialize()
    self.entity_sigs.update:on(function(...)
        return self:update_entity(...)
    end)
    self.entity_sigs.getName:on(function (...)
        return self:get_entity_name(...)
    end)
    self.entity_sigs.getStatus:on(function (...)
        return self:get_entity_status(...)
    end)
end

local function generate_index(id, instance, host_id)
    return (id << 8 | instance) << 8 | host_id
end

function entity_management:generate_unique_instance(obj, host_id)
    local instance = obj['Instance']
    if obj['Slot'] ~= 0xFF then
        -- 防止 obj.Instance 和 obj.Slot 配置问题导致 instance 超过 0xFF
        instance = (instance + obj['Slot'] - 1) & 0xFF
    end

    local index = generate_index(obj['Id'], instance, host_id)
    while self.entities[index] do
        instance = (instance + 1) & 0xFF
        if instance >= 0xFF then break end
        index = generate_index(obj['Id'], instance, host_id)
    end
    return instance
end

function entity_management:register(obj)
    -- 确保 Entity 的 instance 唯一性
    local host_id = obj.BelongsToSystem and obj:get_system_id() or 0
    obj['Instance'] = self:generate_unique_instance(obj, host_id)
    local index = generate_index(obj['Id'], obj['Instance'], host_id)

    -- 初始化 Entity 实例对象，并且进行监听
    local ins = entity_instance.new(obj, index, self.sensor_sigs, host_id)
    ins:listen()

    -- 保存 Entity 实例对象（当前采用 <Id:8, Instance:8, HostId:8>作为key保存当前 Entity 对象），进行后续管理
    self.entities[index] = ins
    utils.push_regist_dump('Entity [%s] of host %s is registered', obj:get_object_name(), host_id)
end

function entity_management:unregister(obj)
    -- 删除当前缓存的资源树对象
    local host_id = obj.BelongsToSystem and obj:get_system_id() or 0
    local index = generate_index(obj['Id'], obj['Instance'], host_id)
    if self.entities[index] then
        self.entities[index] = nil
    end
    log:notice('entity [%s] of host %s has been unregistered', obj:get_object_name(), host_id)
end


function entity_management:update_entity(data)
    if not data.id or not data.instance or not data.host_id then
        log:error('update entity data [%s|%s|%s] is invalid', data.id, data.instance, data.host_id)
        return
    end

    local index = generate_index(data.id, data.instance, data.host_id)
    if self.entities[index] then
        self.entities[index]:update_health(data.last_health, data.cur_health)
    end
end

function entity_management:get_entity_status(data)
    if not data.id or not data.instance or not data.host_id then
        log:error('get entity status data [%s|%s|%s] is invalid', data.id, data.instance, data.host_id)
        return utils.SCAN_ENABLED
    end

    local index = generate_index(data.id, data.instance, data.host_id)
    if not self.entities[index] then
        log:info('get entity status index [0x%06X] is not existed', index)
        return utils.SCAN_ENABLED
    end
    return self.entities[index]:is_disabled() and utils.SCAN_DISABLED or utils.SCAN_ENABLED
end

function entity_management:get_entity_name(id, instance, host_id)
    if not id or not instance or not host_id then
        log:error('get entity name data [%s|%s|%s] is invalid', id, instance, host_id)
        return 'Unknown'
    end

    local index = generate_index(id, instance, host_id)
    local obj = self.entities[index]
    if not obj then
        log:error('entity index [0x%06X] is invalid, get entity name failed', index)
        return 'Unknown'
    end
    return obj.mdb_obj.Name
end

function entity_management:power_ctrl(id, instance, host_id, state)
    if not id or not instance or not host_id then
        log:error('power control data [%s|%s|%s] is invalid', id, instance, host_id)
        return
    end

    local index = generate_index(id, instance, host_id)
    if not self.entities[index] then
        log:error('update entity index [0x%06X] is invalid', index)
        return
    end
    self.entities[index]:power_ctrl(state)
end

return entity_management