-- Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
-- 
-- this file licensed under the 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 signal = require 'mc.signal'
local log = require 'mc.logging'
local enums = require 'basic_cooling.define.cooling_enums'
local props = require 'basic_cooling.define.cooling_properties'

local cooling_device_base = class()

function cooling_device_base:ctor()
    self.objs = {}
    self.obj_add_count = 0
    self.interface = ''
    self.syn_id = ''
    self.class_name = ''
    self.pwm_name = ''

    self.obj_add_complete_signal = signal.new() -- 对象id分发完成不再变化信号
    self.obj_table = {} -- 对象存储表 key为ObjectName
    self.board_add_complete_num = 0 -- 散热板对象分发完成个数
    self.obj_add_complete_flag = false -- 对象id分发完成不再变化标志
    self.board_num = nil -- 当前机型参与调速的散热板个数
    self.board_position_map = {} -- 散热板position表
end

-- 获取对象超时检测
function cooling_device_base:init()
end

function cooling_device_base:get_objs()
    return self.objs
end

function cooling_device_base:get_syn_id()
    return self.syn_id
end

function cooling_device_base:is_obj_add_complete()
    return self.obj_add_complete_flag
end

-- 获取当前散热对象个数
function cooling_device_base:get_obj_count()
    return self.obj_add_count
end

function cooling_device_base:obj_add_callback(class_name, object, position)
    -- 散热板第一次分发散热对象
    if not self.board_position_map[position] then
        log:notice('Receive Fan board %s fan obj start', position)
        self.board_position_map[position] = true
    end
    self.obj_table[object[props.OBJECT_NAME]] = object

    object:get_mdb_object(self.interface).property_changed:on(function(name, value, sender)
        self:props_changed_callback(object, name, value, sender)
    end)
end

function cooling_device_base:props_changed_callback(object, name, value, sender)
    if name == self.syn_id then
        -- 所有散热板都添加分发完成却CoolingObj的Id又更新后，对CoolingObj进行分发缓存
        if self.board_num and self.board_add_complete_num >= self.board_num then
            self:update_cooling_device_obj_identify_result()
        end
    end
end

function cooling_device_base:update_objs_recived_status(fan_board_num, position)
    -- FanBoardNum挂在psr的CoolingConfig上，此处传入的可能为nil
    -- MaxPumpNum挂在psr的LiquidConfig上，此处传入的可能为nil
    self.board_num = fan_board_num and fan_board_num or self.board_num
    if self.board_position_map[position] then
        self.board_add_complete_num = self.board_add_complete_num + 1
        log:notice('Receive cooling board %s cooling obj complete, cur num: %s, cooling board num: %s',
            position, self.board_add_complete_num, self.board_num)
    end
    -- 当所有散热板都分发完成后，进行初次CoolingObj进行分发缓存
    -- 散热板卸载重加载场景会收到重复的散热板sr添加完成信号
    if self.board_num and self.board_add_complete_num >= self.board_num then
        self:update_cooling_device_obj_identify_result()
    end
end

-- 当同一类型调速器件的id都不重复，则认为对象识别完成
function cooling_device_base:update_cooling_device_obj_identify_result()
    local device_id_map = {}
    local device_id_string = ''
    self.obj_add_count = 0

    for _, fan_obj in pairs(self.obj_table) do
        -- 出现重复散热id，表示散热对象未同步完成
        if device_id_map[fan_obj[self.syn_id]] then
            self.obj_add_count = 0
            self.objs = {}
            return
        end
        self.obj_add_count = self.obj_add_count + 1
        device_id_map[fan_obj[self.syn_id]] = true
        self.objs[fan_obj[self.syn_id]] = fan_obj
    end

    -- 散热对象id同步成功后打印一次
    if not self.obj_add_complete_flag then
        for obj_id, _ in pairs(self.objs) do
            device_id_string = device_id_string .. ' ' ..  obj_id
        end
        log:notice('%s object id identify result: %s', self.class_name, device_id_string)
    end

    self.obj_add_complete_flag = true
    self.obj_add_complete_signal:emit()
end

function cooling_device_base:init_cooling_device_level(level)
    for _, cooling_device_o in pairs(self.objs) do
        cooling_device_o.Level = level
    end
end

function cooling_device_base:obj_delete_callback(class_name, object, position)
    self.obj_table[object[props.OBJECT_NAME]] = nil
    self.objs[object[self.syn_id]] = nil
    self.obj_add_count = self.obj_add_count - 1
end

-- 获取所有调速器件的手动转速
function cooling_device_base:get_all_cooling_device_manual_level()
    local device_level_table = {}
    local max_id = 0
    for id, obj in pairs(self.objs) do
        device_level_table[id] = obj.Level
        max_id = math.max(max_id, id)
    end
    for i = 1, max_id, 1 do
        if not device_level_table[i] then
            device_level_table[i] = 0
        end
    end
    return device_level_table
end

-- 调速器件的实际转速与预期手动转速是否一致
function cooling_device_base:is_hardware_pwm_equal_level()
    local MAX_HARDWARE_PWM = 255
    local MAX_LEVEL = 100
    for _, obj in pairs(self.objs) do
        if obj.HardwarePWM ~= math.floor(obj.Level * MAX_HARDWARE_PWM / MAX_LEVEL + 0.5) then
            return false
        end
    end
    return true
end

function cooling_device_base:set_cooling_device_manual_level(device_level_table)
    for id, level in pairs(device_level_table) do
        if self.objs[id] then
            self.objs[id].Level = level
        end
    end
end

-- 获取不连续的散热设备手动转速表(器件id存在跳变场景)
function cooling_device_base:get_discontinuous_cooling_device_manual_level()
    local device_level_table = {}
    for id, obj in pairs(self.objs) do
        device_level_table[id] = obj.Level
    end
    return device_level_table
end

-- 获取不连续的散热设备的实际转速表(器件id存在跳变场景)
function cooling_device_base:get_discontinuous_cooling_device_actual_level()
    local device_level_table = {}
    for _, obj in pairs(self.objs) do
        local max_supported_pwm = obj.MaxSupportedPWM
        -- 防止未定义获取为nil和除0
        if not max_supported_pwm or max_supported_pwm == 0 then
            log:error('The %s(%s) property MaxSupportedPWM(%s) is invalid.',
                self.class_name, obj[self.syn_id], max_supported_pwm)
            max_supported_pwm = 255
        end
        device_level_table[obj[self.syn_id]] = obj[self.pwm_name] * 100 // max_supported_pwm
    end
    return device_level_table
end

-- 获取单个调速器件的手动转速
function cooling_device_base:get_single_cooling_device_manual_level(id)
    if self.objs[id] then
        return self.objs[id].Level
    end
    return nil
end

-- 获取单个调速器件的实际转速
function cooling_device_base:get_single_cooling_device_actual_level(id)
    if self.objs[id] then
        local max_supported_pwm = self.objs[id].MaxSupportedPWM
        -- 防止未定义获取为nil和除0
        if math.type(max_supported_pwm) ~= enums.data_type.INTEGER or max_supported_pwm <= 0 then
            log:error('The %s(%s) property MaxSupportedPWM(%s) is invalid.',
                self.class_name, id, max_supported_pwm)
            max_supported_pwm = 255
        end
        return self.objs[id][self.pwm_name] * 100 // max_supported_pwm
    end
    return nil
end

function cooling_device_base:set_all_cooling_device_manual_level(level)
    for _, obj in pairs(self.objs) do
        obj.Level = level
    end
end

function cooling_device_base:set_single_cooling_device_manual_level(id, level)
    if not self.objs[id] then
        log:error("The %s whose id %s does not exist", self.class_name, id)
        return false
    end
    self.objs[id].Level = level
    return true
end

-- 返回一个指定转速的手动转速table给批量下发接口使用
function cooling_device_base:get_same_manual_level_table(value)
    local manual_level_table = {}
    local max_id = 0 -- 需保证拿到的最大id是正确的
    for id, _ in pairs(self.objs) do
        manual_level_table[id] = value
        max_id = math.max(max_id, id)
    end
    -- 多风扇板机型id跳变，未拿到对象的位置置为0以保证table连续
    for i = 1, max_id, 1 do
        if not manual_level_table[i] then
            manual_level_table[i] = 0
        end
    end
    return manual_level_table
end

return cooling_device_base