-- Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. 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 skynet = require 'skynet'
local log = require 'mc.logging'
local client = require 'thermal_mgmt.client'
local props = require 'basic_cooling.define.cooling_properties'
local enums = require 'basic_cooling.define.cooling_enums'
local abn_pumps = require "basic_cooling.cooling_abnormal_device.abnormal_pump"

local pump_data_keeping = class()

function pump_data_keeping:ctor(bus)
    self.bus = bus
    self.data = nil
    self.props_table = {
        [props.PUMP_PRESENCE] = true,
        [props.PUMP_STATUS] = true,
        [props.PUMP_ID] = true
    }
    self.interface_table = {
        ['bmc.kepler.Systems.Pump'] = client.ForeachPumpObjects
    }
    self.path = '/bmc/kepler/Systems/1/Thermal/Pumps'
    self.abn_pumps_instance = abn_pumps.get_instance()
    self.thermal_pumps_obj = nil
end

function pump_data_keeping:init()
    self.slot = self:listenning()
end

function pump_data_keeping:get_obj()
    if not self.thermal_pumps_obj then
        client:ForeachPumpsObjects(function (obj)
            if obj.SetPWM_PACKED then
                self.thermal_pumps_obj = obj
                return
            end
        end)
    end
    return self.thermal_pumps_obj
end

-- 批量设置泵转速，0设置成功 1设置失败
function pump_data_keeping:set_pumps_level(ctx, pump_table)
    if not self:get_obj() then
        return 1
    end
    local ok, rsp = pcall(self.thermal_pumps_obj.SetPWM_PACKED, self.thermal_pumps_obj, ctx, pump_table)
    if not ok then
        log:debug("Set all pump pwm failed, err_msg: %s", rsp)
        return 1
    else
        log:debug("Set all pump pwm(%s) success", table.concat(pump_table, ' ', 1))
    end
    return rsp.Result
end

function pump_data_keeping:get_object_infos()
    local tmp_info = {} -- 建立临时的table存储,防止异常导致数据丢失
    client:ForeachPumpObjects(function (obj)
        tmp_info[obj.path] = {
                Id = obj.Id,
                Status = obj.Status,
                Presence = obj.Presence
            }
        log:debug('[cooling] Get pump(id:%s) info: path:%s, status:%s, presence:%s ',
            obj.Id, obj.path, obj.Status, obj.Presence)
    end)
    self.data = tmp_info
end

-- 同步资源树对象数据
function pump_data_keeping:sync_obj_info(targ_obj, sync_obj)
    for prop, _ in pairs(self.props_table) do
        if sync_obj[prop] ~= nil then
            targ_obj[prop] = sync_obj[prop]
            log:notice('Sync pump object prop(%s): %s', prop, tostring(sync_obj[prop]))
        end
    end
end

function pump_data_keeping:listenning()
    return client:OnPumpPropertiesChanged(function(prop_value, path, interface)
        self:interface_props_changed_callback(path, interface, prop_value)
    end)
end


function pump_data_keeping:get_pump_info()
    -- 每分钟从资源树获取一次泵信息并检查异常状态，刷新异常调速策略
    skynet.fork_once(function()
        while true do
            self:get_object_infos()
            for _ , object in pairs(self.data) do
                local id = object[props.PUMP_ID]
                local status = object[props.PUMP_STATUS]
                self:update_exp_info(id, status)
            end
            skynet.sleep(6000)
        end
    end)
end

-- 对象添加回调
function pump_data_keeping:interface_added_callback(sender, path, interfaces_and_properties)
    for interface, _ in pairs(interfaces_and_properties) do
        if self.interface_table[interface] then
            log:notice('Add Pump object(%s), interface: %s', path, interface)
            self:get_object_info_by_interface(path, interface)
        end
    end
end

-- 对象删除回调
function pump_data_keeping:interface_removed_callback(sender, path, interfaces)
    for _, interface in pairs(interfaces) do
        if interface == 'bmc.kepler.Systems.Pumps' then
            self.thermal_pumps_obj = nil
            log:notice("Delete Pumps obj(path:%s)", path)
            return
        end
        if self.interface_table[interface] then
            log:notice('Remove object(%s), interface: %s', path, interface)
            self.data[path] = nil
            return
        end
    end
end

function pump_data_keeping:get_object_info_by_interface(path, interface)
    -- 存在对象，初始化data为table
    if not self.data then
        self.data = {}
    end

    -- 初始化data，key为路径存储对象信息
    if not self.data[path] then
        self.data[path] = {}
    end

    local obj
    self.interface_table[interface](client, function (object)
        if object.path == object.path then
            obj = object
        end
    end)
    if not obj then
        log:error("Get object(%s) failed, interface(%s)", path, interface)
        return
    end
    self:sync_obj_info(self.data[path], obj)
    log:notice('Add Pump object(%s) completed, interface: %s', path, interface)
end

-- 监听回调函数
function pump_data_keeping:interface_props_changed_callback(path, interface, changed_props)
    if not self.interface_table[interface] then
        return
    end

    local data_info
    local id
    local status
    if self.data and self.data[path]then
        data_info = self.data[path]
    else
        self:get_object_info_by_interface(path, interface)
        id = self.data[path][props.PUMP_ID]
        status = self.data[path][props.PUMP_STATUS]
        self:update_exp_info(id, status)
        return
    end

    for prop, value in pairs(changed_props) do
        if self.props_table[prop] then
            data_info[prop] = value:value()
            log:notice('Object(%s) prop(%s) changed: %s', path, prop, tostring(value:value()))
        end

        if prop == props.PUMP_STATUS then
            id = self.data[path][props.PUMP_ID]
            status = self.data[path][props.PUMP_STATUS]
            self:update_exp_info(id, status)
        end
    end
end

function pump_data_keeping:update_exp_info(id, status)
    local abn_status = self.abn_pumps_instance:get_pump_status(id, status)
    if abn_status == nil then
        return
    end
    self.abn_pumps_instance:update_exp_speed_table(enums.exp_type.ABNORMAL_PUMP, id, abn_status)
end

return pump_data_keeping