-- 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 log = require 'mc.logging'
local class = require 'mc.class'
local Singleton = require 'mc.singleton'
local enums = require 'basic_cooling.define.cooling_enums'
local utils = require 'basic_cooling.cooling_utils'

local abnormal_pump = class()

function abnormal_pump:ctor()
    self.objs = {}
    self.exp_speed_t = {}
    self.abnormal_status_mapping = {
        [0] = 'Normal',
        [1] = 'AbnormalRotation',
        [2] = 'NotInPosition',
        [3] = 'StopWorking'
    }
end

function abnormal_pump:init()
    self.signal_pump_tb = {}
end

function abnormal_pump:get_exp_policy_table()
    return self.exp_speed_t
end

function abnormal_pump:get_abnormal_pump_obj(id, model)
    if not self.objs[id] then
        return
    end
    return self.objs[id][model]
end

function abnormal_pump:obj_add_callback(class_name, object, position)
    if not self.objs[object.DeviceId] then
        self.objs[object.DeviceId] = {}
    end
    self.objs[object.DeviceId][object.Models] = object
    log:notice('Add object callback, class_name:%s, position:%s, DeviceId:%s, Models:%s',
        class_name, position, object.DeviceId, object.Models)
end

function abnormal_pump:obj_delete_callback(class_name, object, position)
    if self.objs[object.DeviceId] and self.objs[object.DeviceId][object.Models] then
        self.objs[object.DeviceId][object.Models] = nil
    end
    if self.objs[object.DeviceId] and not next(self.objs[object.DeviceId]) then
        self.objs[object.DeviceId] = nil
    end
end

-- 根据异常类型、对象id等信息生成唯一的键
function abnormal_pump.gen_exp_speed_key(exp_type, obj_id, abnormal_model)
    if exp_type == enums.exp_type.ABNORMAL_PUMP then
        return string.format('<%s:%s:%s>', exp_type, obj_id, abnormal_model)
    else
        log:error('Unintended exception type')
        return nil
    end
end

function abnormal_pump:add_exp_speed_t(exp_speed_key, abn_policy_pump_table)
    if self.exp_speed_t[exp_speed_key] then
        log:error('Duplicative exp speed, key:%s', exp_speed_key)
        return
    end
    self.exp_speed_t[exp_speed_key] = abn_policy_pump_table
    local abn_policy_info = utils.parse_table_info(abn_policy_pump_table)
    log:notice('Add exception speed, key:(%s), policy(%s)', exp_speed_key, abn_policy_info)
end

function abnormal_pump:delete_exp_speed_t(exp_speed_key)
    if not self.exp_speed_t[exp_speed_key] then
        return
    end
    local abn_policy_pump_table = self.exp_speed_t[exp_speed_key]
    local abn_policy_info = utils.parse_table_info(abn_policy_pump_table)
    log:notice('Delete exception speed, key:(%s), policy(%s)', exp_speed_key, abn_policy_info)
    self.exp_speed_t[exp_speed_key] = nil
end

function abnormal_pump:get_pump_status(id, status)
    if self.abnormal_status_mapping[status] == nil then
        log:notice("Pump(id:%s) status:%s is unknown", id, status)
        return nil
    end

    if self.abnormal_status_mapping[status] ~= 'Normal' then
        log:notice("Pump(id:%s) is abnormal, state:%s", id, self.abnormal_status_mapping[status])
    end

    return self.abnormal_status_mapping[status]
end

function abnormal_pump:generate_abn_policy_table(pump_table, pump_pwms)
    local abn_policy_pump_table = {}
    for key, device_id in pairs(pump_table) do
        if type(pump_pwms) == "table" then
            abn_policy_pump_table[device_id] = pump_pwms[key]
        end

        if type(pump_pwms) == "number" then
            abn_policy_pump_table[device_id] = pump_pwms
        end
    end
    return abn_policy_pump_table
end

function abnormal_pump:refresh_exp_speed_table(exp_type, id)
    for _, level in pairs(self.abnormal_status_mapping) do
        local key = self.gen_exp_speed_key(exp_type, id, level)
        self:delete_exp_speed_t(key)
    end
end

function abnormal_pump:update_exp_speed_table(exp_type, id, device_status)
    -- 恢复正常，将同obj_id下同类型异常转速信息删除
    if device_status == 'Normal' then
        self:refresh_exp_speed_table(exp_type, id)
        return
    end

    -- 异常状态
    local abn_pump_obj = self:get_abnormal_pump_obj(id, device_status)
    if not abn_pump_obj then
        log:notice("Get abnormal policy(status:%s) for pump(id:%s) failed", exp_type, id)
        return
    end
    local abn_policy_pump_table = {}

    local exp_speed_key = self.gen_exp_speed_key(exp_type, id, device_status)
    -- 加入新的异常转速信息
    if not self.exp_speed_t[exp_speed_key] then
        self:refresh_exp_speed_table(exp_type, id)
        local pump_table = abn_pump_obj.LiquidCoolingIds
        local pump_pwms = abn_pump_obj.LiquidCoolingPWMs
        abn_policy_pump_table = self:generate_abn_policy_table(pump_table, pump_pwms)
        self:add_exp_speed_t(exp_speed_key, abn_policy_pump_table)
    end
end

function abnormal_pump:pump_status_signal_subscribed(pump_obj)
    if self.signal_pump_tb[pump_obj.id] then
        return
    end
    local abn_status
    self.signal_pump_tb[pump_obj.Id] = pump_obj.pump_status_changed:on(function(info, value)
        if info == 'Status' then
            abn_status = self:get_pump_status(value.Id, value.Status)
            if abn_status == nil then
                return
            end
            self:update_exp_speed_table(enums.exp_type.ABNORMAL_PUMP, value.Id, abn_status)
        end
    end)
end

return Singleton(abnormal_pump)
