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

require "thermal_mgmt.json_types.CoolingConfig"
local props = require 'basic_cooling.define.cooling_properties'
local cooling_enums = require 'basic_cooling.define.cooling_enums'
local custom_msg = require 'messages.custom'
local base_msg = require 'messages.base'
local Singleton = require 'mc.singleton'
local air_config = require 'basic_cooling.cooling_config.air_config'
local cooling_fans = require 'basic_cooling.cooling_device.cooling_fans'
local cooling_config_base = require 'basic_cooling.cooling_config.base'
local external_data_keeping = require 'basic_cooling.data_keeping.external_data_keeping'


local fans_config = class(cooling_config_base)

local infos_prop_map = props.DISK_INFO_PROP_MAP
local prop_table = props.DISK_PROP_TABLE

function fans_config:ctor()
    self.obj = nil
    self.persistance = {}
    self.disks_data_keeping = external_data_keeping.get_instance().data_keeping_objs.disks_data_keeping
    self.cooling_fans = cooling_fans.get_instance()
    self.manual_mode_status_table = {
        last_timeout = nil, -- 上次更新的timeout
        real_timeout = nil, -- 用于业务逻辑计算的超时时间
        expir_time = nil,   -- timeout结束的时间
        pre_manual_level = nil,
        pre_smt_cl_mode = nil,
        power_status_bck = nil,
        manual_level_set_flag = false -- 手动转速是否已下发标志，true需要下发,false已经下发
    }
    self.air_config_instance = air_config.get_instance()
end

function fans_config:set_obj(object)
    self.obj = object
    self.smart_cooling_mode = self.obj[props.SMART_COOLING_MODE]
    self.original_smart_cooling_mode = self.obj[props.ORIGINAL_SMART_COOLING_MODE]
    object:get_mdb_object('bmc.kepler.Systems.CoolingConfig').property_before_change:on(function(name, value, sender)
        return true
    end)
    object:get_mdb_object('bmc.kepler.Systems.CoolingConfig').property_changed:on(function(name, value, sender)
        self:props_changed_callback(object, name, value, sender)
    end)

    self.disks_data_keeping.disks_info_changed:on(function(info, value)
        if info == 'hdd_temp' and self.obj then
            self.obj.SysHDDsMaxTemperature = value
        elseif info == 'ssd_temp' and self.obj then
            self.obj.SysSSDsMaxTemperature = value
        elseif info == 'disks_temp_available' and self.obj then
            self.obj.DiskRowTemperatureAvailable = value
        elseif info == 'all_ssd_temp' then
            self.obj.SysAllSSDsMaxTemperature = value
        end

        -- 注册触发信号时的回调函数，当对应类型的硬盘温度变化时，为coolingconfig对应字段赋值
        self:set_coolingconfig_prop_by_info_signal(info, value)
    end)
    self.obj.SysHDDsMaxTemperature = self.disks_data_keeping.SysHDDsMaxTemperature
    self.obj.SysSSDsMaxTemperature = self.disks_data_keeping.SysSSDsMaxTemperature
    self.obj.DiskRowTemperatureAvailable = self.disks_data_keeping.DiskRowTemperatureAvailable
    self.obj.SysAllSSDsMaxTemperature = self.disks_data_keeping.SysAllSSDsMaxTemperature

    -- 遍历表，为coolingconfig对应字段赋值
    self:set_coolingconfig_prop_by_info()
end

function fans_config:get_mixed_supported()
    if not self.obj then
        return nil
    end
    return self.obj[props.MIXED_MODE_SUPPORTED]
end

function fans_config:props_changed_callback(object, name, value, sender)
    if name == props.SMART_COOLING_MODE then
        self.smart_cooling_mode = self.obj[props.SMART_COOLING_MODE]
        self.original_smart_cooling_mode = self.obj[props.ORIGINAL_SMART_COOLING_MODE]
    elseif name == props.INIT_LEVEL_IN_STARTUP then
        self.air_config_instance:set_init_level_in_startup(value)
    elseif name == "MinAllowedFanSpeedPercent" then
        self.air_config_instance:set_min_allowed_speed_percent(value)
    elseif name == props.CTL_MODE then
        self.air_config_instance:set_ctrl_mode(value)
    elseif name == props.TIME_OUT then
        self.air_config_instance:set_manual_timeout(value)
    elseif name == props.CONF_MANUAL_LEVEL then
        self.air_config_instance:set_manual_level(value)
    end
end

function fans_config:get_manual_mode_status_table()
    return self.manual_mode_status_table
end

function fans_config:get_smart_cooling_mode()
    return self.smart_cooling_mode
end

function fans_config:set_init_level_in_startup(value)
    self.obj[props.INIT_LEVEL_IN_STARTUP] = value
end

function fans_config:get_init_level_in_startup()
    return self.obj[props.INIT_LEVEL_IN_STARTUP]
end

function fans_config:get_disk_row_temp_available()
    return self.obj.DiskRowTemperatureAvailable
end

function fans_config:get_sensor_location_supported()
    return self.obj[props.SENSOR_LOCATION_SUPPORTED]
end

function fans_config:is_smart_cooling_enabled()
    return self.obj.SmartCoolingState == cooling_enums.smart_cooling_state.ENABLED
end

local function set_obj_prop(obj, prop_name, value)
    obj[prop_name] = value
end

-- 设置控制模式(手动/自动)
function fans_config:set_ctrl_mode(value)
    if not self.obj then
        return false
    end
    self.obj[props.CTL_MODE] = value
    return true
end

-- 设置超时时间
function fans_config:set_manual_timeout(value)
    if not self.obj then
        return false
    end
    self.obj[props.TIME_OUT] = value
    return true
end

-- 设置风扇手动转速
function fans_config:set_manual_level(value)
    local ctrl_mode_persist_type = self.air_config_instance:get_ctrl_mode_persist_type()
    self.obj[props.CONF_MANUAL_LEVEL] = value
    self.air_config_instance:set_manual_level_persist(value)
    log:notice("Current manual speed percent is %s, control mode persist type is %s", value, ctrl_mode_persist_type)
    self.manual_mode_status_table.manual_level_set_flag = true
    self.cooling_fans:set_all_cooling_device_manual_level_persist(value, ctrl_mode_persist_type)
    return true
end

function fans_config:set_manual_level_start(value)
    local ctrl_mode_persist_type = self.air_config_instance:get_ctrl_mode_persist_type()
    local ctrl_mode = self.air_config_instance:get_ctrl_mode_persist()
    self.obj[props.CONF_MANUAL_LEVEL] = value
    self.air_config_instance:set_manual_level_persist(value)
    log:notice("Init manual speed percent is %s, control mode persist type is %s", value, ctrl_mode_persist_type)
    self.cooling_fans:set_all_cooling_device_manual_level_persist_start(ctrl_mode, value, ctrl_mode_persist_type)
    return true
end

function fans_config:set_smartcooling_mode(mode, change_flag)
    local config_o = self.obj
    local cur_medium = config_o[props.COOLING_MEDIUM]
    local cooling_modes = cooling_enums.smart_cooling_mode
    local switch = {}

    if cur_medium == cooling_enums.cooling_mediums.AIR_COOLING then
        switch = {
            [cooling_modes.COOLING_ENERGY_SAVING_MODE] = true,
            [cooling_modes.COOLING_LOW_NOISE_MODE] = true,
            [cooling_modes.COOLING_HIGH_PERFORMANCE_MODE] = true,
            [cooling_modes.COOLING_CUSTOM_MODE] = true
        }
    elseif cur_medium == cooling_enums.cooling_mediums.LIQUID_COOLING then
        switch = {
            [cooling_modes.COOLING_CUSTOM_MODE] = true,
            [cooling_modes.COOLING_LIQUID_MODE] = true
        }
    else
        log:error("Invalid medium, medium:%s", cur_medium)
        return false, base_msg.InternalError()
    end

    if not self:is_smart_cooling_enabled() then
        log:error("Smart cooling mode is disabled")
        return false, custom_msg.PropertyModificationNotSupported('%Mode')
    end

    if switch[mode] then
        if (change_flag & cooling_enums.smart_cooling_changed.ORIGINAL) ~= 0 then
            if not pcall(set_obj_prop, config_o, props.ORIGINAL_SMART_COOLING_MODE, mode) then
                log:error("Set property(%s) failed", props.SMART_COOLING_MODE)
                return false, base_msg.InternalError()
            end
        end
        if (change_flag & cooling_enums.smart_cooling_changed.BASIC) ~= 0 then
            if not pcall(set_obj_prop, config_o, props.SMART_COOLING_MODE, mode) then
                log:error("Set property(%s) failed", props.SMART_COOLING_MODE)
                return false, base_msg.InternalError()
            end
        end
    else
        log:error("Invalid mode, medium:%s, mode:%s", cur_medium, mode)
        return false, base_msg.PropertyValueNotInList(tostring(mode), '%Mode')
    end
    return true, nil
end

function fans_config:set_medium(medium)
    local config_o = self.obj
    if config_o[props.COOLING_MEDIUM] ~= medium then
        local ok, res = pcall(set_obj_prop, config_o, props.COOLING_MEDIUM, medium)
        if not ok then
            log:error("Set property(%s) failed, err:%s", props.COOLING_MEDIUM, res)
            error(base_msg.InternalError())
        end
    end

    -- 散热介质改变后，修改smartcooling mode
    local smart_cooling_mode = (medium == cooling_enums.cooling_mediums.LIQUID_COOLING) and
        cooling_enums.smart_cooling_mode.COOLING_LIQUID_MODE or
        cooling_enums.smart_cooling_mode.COOLING_ENERGY_SAVING_MODE

    return self:set_smartcooling_mode(smart_cooling_mode, cooling_enums.smart_cooling_changed.BOTH)
end

-- 设置风扇模式为手动，手动转速为fan_level
function fans_config:set_manual_mode_and_level(fan_level)
    self.obj[props.TIME_OUT] = 100000000 -- 最大超时时间为100000000
    self.obj[props.CTL_MODE] = cooling_enums.modes.Manual
    self.obj[props.CONF_MANUAL_LEVEL] = fan_level
    self.cooling_fans:set_all_cooling_device_manual_level(fan_level)
end

function fans_config:mark_cooling_mode()
    self.persistance = {
        [props.CTL_MODE] = self.obj[props.CTL_MODE],
        [props.TIME_OUT] = self.obj[props.TIME_OUT],
        [props.CONF_MANUAL_LEVEL] = self.obj[props.CONF_MANUAL_LEVEL]
    }
end

function fans_config:recover_mark_cooling_mode()
    for prop, value in pairs(self.persistance) do
        self.obj[prop] = value
    end
end

function fans_config:is_disk_temp_available()
    return self.obj.DiskRowTemperatureAvailable
end

-- 获取当前的风扇板个数
function fans_config:get_fan_board_num()
    if not self.obj then
        return nil
    end
    return self.obj[props.FAN_BOARD_NUM]
end

function fans_config:get_medium()
    if not self.obj then
        return nil
    end
    return self.obj[props.COOLING_MEDIUM]
end

function fans_config:get_min_speed_enabled()
    if not self.obj then
        return nil
    end
    return self.obj[props.MIN_FAN_SPEED_ENABLED]
end

function fans_config:set_min_speed_enabled(value)
    self.obj[props.MIN_FAN_SPEED_ENABLED] = value
end

function fans_config:get_min_speed_percent()
    if not self.obj then
        return nil
    end
    return self.obj[props.MIN_FAN_SPEED_PERCENT]
end

function fans_config:set_min_speed_percent(value)
    self.obj[props.MIN_FAN_SPEED_PERCENT] = value
end

function fans_config:set_coolingconfig_prop_by_info_signal(info, value)
    -- fans_obj.obj表示coolingconfig对象
    if  self.obj and infos_prop_map[info] then
        self.obj[infos_prop_map[info]] = value
    end
end

function fans_config:set_coolingconfig_prop_by_info()
    for _, prop in pairs(prop_table) do
        self.obj[prop] = self.disks_data_keeping[prop]
    end
end

function fans_config:get_coolingconfig_minimal_level()
    if not self.obj then
        return nil
    end
    return self.obj[props.MIN_LIMIT_FAN_SPEED]
end

function fans_config:get_coolingconfig_max_level()
    if not self.obj then
        return nil
    end
    return self.obj[props.MAX_LIMIT_FAN_SPEED]
end
return Singleton(fans_config)