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

local class = require 'mc.class'
local c_handler_base = require 'bma.handles.handler_base'
local c_optical_module = require 'device.class.optical_module'
local c_handler_optical = require 'bma.handles.handler_optical'
local singleton = require 'mc.singleton'
local get_value_to_update = c_handler_base.get_value_to_update
local update_presence = c_handler_optical.update_presence
local set_presence_to_device_obj = c_handler_optical.set_presence_to_device_obj
local optical_module_mgmt = require 'device.class.nic_mgmt.om_mgmt'
local json = require 'cjson'
local json_null = json.null

local get_odata_id = c_handler_base.get_odata_id

local c_handler_optical_diag = class(c_handler_base)

function c_handler_optical_diag:ctor()
end

function c_handler_optical_diag:init()
    self:regist_odata_type('OemEthernetInterfaceSffDiagnostic')
    self:regist_odata_type('OemInfiniBandSffDiagnostic')
    self:regist_odata_type('OemFCSffDiagnostic')

    self:regist_class_type(c_optical_module)

    c_handler_optical.get_instance().on_need_update:on(function(object, path)
        self:match_resource(object, path)
    end)
end

function c_handler_optical_diag:find_object(_, data)
    local optical_bma_id = self.get_parent_path(get_odata_id(data))
    return c_handler_optical.get_instance():get_object(optical_bma_id)
end

local function set_temperature_to_device_obj(op, temperature)
    if not temperature then
        return
    end
    if temperature == json_null then
        temperature = 65535
    end
    pcall(function ()
        optical_module_mgmt.get_instance():set_temperature_to_device_obj(op, temperature)
    end)
end

local function set_supply_voltage_to_device_obj(op, voltage)
    if not voltage then
        return
    end
    if voltage == json_null then
        voltage = 65535
    end
    pcall(function ()
        optical_module_mgmt.get_instance():set_supply_voltage_to_device_obj(op, voltage)
    end)
end

local function set_voltage_lower_threshold_critical_to_device_obj(op, voltage)
    if not voltage then
        return
    end
    if voltage == json_null then
        voltage = 65535
    end
    pcall(function ()
        optical_module_mgmt.get_instance():set_voltage_lower_threshold_critical_to_device_obj(op, voltage)
    end)
end

local function set_voltage_upper_threshold_critical_to_device_obj(op, voltage)
    if not voltage then
        return
    end
    if voltage == json_null then
        voltage = 65535
    end
    pcall(function ()
        optical_module_mgmt.get_instance():set_voltage_upper_threshold_critical_to_device_obj(op, voltage)
    end)
end

local function update_temperature_upper_threshold_critical_from_bma(op, data, default)
    if data.TempHighAlarmCelsius == nil then
        return
    end
    op.TemperatureUpperThresholdCritical = get_value_to_update(op.TemperatureUpperThresholdCritical,
        data.TempHighAlarmCelsius, default)
    pcall(function()
        optical_module_mgmt.get_instance():set_temperature_upper_threshold_critical_to_device_obj(
            op, op.TemperatureUpperThresholdCritical)
    end)
end

local function update_temperature_lower_threshold_critical_from_bma(op, data, default)
    if data.TempLowAlarmCelsius == nil then
        return
    end
    op.TemperatureLowerThresholdCritical = get_value_to_update(op.TemperatureLowerThresholdCritical,
        data.TempLowAlarmCelsius, default)
    pcall(function()
        optical_module_mgmt.get_instance():set_temperature_lower_threshold_critical_to_device_obj(
            op, op.TemperatureLowerThresholdCritical)
    end)
end

local function update_tx_bias_current_milli_amps_from_bma(op, data, default_arr)
    if data.TxBiasCurrentMilliampere == nil then
        return
    end
    op.TXBiasCurrentMilliAmps = get_value_to_update(op.TXBiasCurrentMilliAmps,
        data.TxBiasCurrentMilliampere, default_arr)
    pcall(function()
        optical_module_mgmt.get_instance():set_tx_bias_current_milli_amps_to_device_obj(op, op.TXBiasCurrentMilliAmps)
    end)
end

local function update_bc_tx_upper_threshold_critical_from_bma(op, data, default)
    if data.TxBiasHighAlarmMilliampere == nil then
        return
    end
    op.BC_TXUpperThresholdCritical = get_value_to_update(op.BC_TXUpperThresholdCritical,
        data.TxBiasHighAlarmMilliampere, default)
    pcall(function()
        optical_module_mgmt.get_instance():set_bias_current_upper_threshold_critical_to_device_obj(
            op, op.BC_TXUpperThresholdCritical)
    end)
end

local function update_bc_tx_lower_threshold_critical_from_bma(op, data, default)
    if data.TxBiasLowAlarmMilliampere == nil then
        return
    end
    op.BC_TXLowerThresholdCritical = get_value_to_update(op.BC_TXLowerThresholdCritical,
        data.TxBiasLowAlarmMilliampere, default)
    pcall(function()
        optical_module_mgmt.get_instance():set_bias_current_lower_threshold_critical_to_device_obj(
            op, op.BC_TXLowerThresholdCritical)
    end)
end

local function update_tx_output_power_milli_watts_from_bma(op, data, default_arr)
    if data.TxPowerCurrentMilliwatt == nil then
        return
    end
    op.TXOutputPowerMilliWatts = get_value_to_update(op.TXOutputPowerMilliWatts,
        data.TxPowerCurrentMilliwatt, default_arr)
    pcall(function()
        optical_module_mgmt.get_instance():set_tx_output_power_milli_watts_to_device_obj(op, op.TXOutputPowerMilliWatts)
    end)
end

local function update_power_tx_upper_threshold_critical_from_bma(op, data, default)
    if data.TxPowerHighAlarmMilliwatt == nil then
        return
    end
    op.Power_TXUpperThresholdCritical = get_value_to_update(op.Power_TXUpperThresholdCritical,
        data.TxPowerHighAlarmMilliwatt, default)
    pcall(function()
        optical_module_mgmt.get_instance():set_tx_power_upper_threshold_critical_to_device_obj(
            op, op.Power_TXUpperThresholdCritical)
    end)
end

local function update_power_tx_lower_threshold_critical_from_bma(op, data, default)
    if data.TxPowerLowAlarmMilliwatt == nil then
        return
    end
    op.Power_TXLowerThresholdCritical = get_value_to_update(op.Power_TXLowerThresholdCritical,
        data.TxPowerLowAlarmMilliwatt, default)
    pcall(function()
        optical_module_mgmt.get_instance():set_tx_power_lower_threshold_critical_to_device_obj(
            op, op.Power_TXLowerThresholdCritical)
    end)
end

local function update_rx_input_power_milli_watts_from_bma(op, data, default_arr)
    if data.RxPowerCurrentMilliwatt == nil then
        return
    end
    op.RXInputPowerMilliWatts = get_value_to_update(op.RXInputPowerMilliWatts,
        data.RxPowerCurrentMilliwatt, default_arr)
    pcall(function()
        optical_module_mgmt.get_instance():set_rx_input_power_milli_watts_to_device_obj(op, op.RXInputPowerMilliWatts)
    end)
end

local function update_rx_upper_threshold_critical_from_bma(op, data, default)
    if data.RxPowerHighAlarmMilliwatt == nil then
        return
    end
    op.RXUpperThresholdCritical = get_value_to_update(op.RXUpperThresholdCritical,
        data.RxPowerHighAlarmMilliwatt, default)
    pcall(function()
        optical_module_mgmt.get_instance():set_rx_power_upper_threshold_critical_to_device_obj(
            op, op.RXUpperThresholdCritical)
    end)
end

local function update_rx_lower_threshold_critical_from_bma(op, data, default)
    if data.RxPowerLowAlarmMilliwatt == nil then
        return
    end
    op.RXLowerThresholdCritical = get_value_to_update(op.RXLowerThresholdCritical,
        data.RxPowerLowAlarmMilliwatt, default)
    pcall(function()
        optical_module_mgmt.get_instance():set_rx_power_lower_threshold_critical_to_device_obj(
            op, op.RXLowerThresholdCritical)
    end)
end

function c_handler_optical_diag:update_optical_diag(op, data)
    op.get_diagnostic_by_ibma_success = true
    if op and op:check_port_workload_type() then
        return
    end
    local default = 65535
    local default_arr = {}
    local default_num = 0
    -- bmc.kepler.Systems.OpticalModule.Temperature
    op.ReadingCelsius = get_value_to_update(op.ReadingCelsius, data.TempCurrentCelsius, default)
    set_temperature_to_device_obj(op, data.TempCurrentCelsius)
    update_temperature_upper_threshold_critical_from_bma(op, data, default)
    update_temperature_lower_threshold_critical_from_bma(op, data, default)

    -- bmc.kepler.Systems.OpticalModule.Voltage
    op.SupplyVoltage = get_value_to_update(op.SupplyVoltage, data.VccCurrentVolts, default)
    set_supply_voltage_to_device_obj(op, data.VccCurrentVolts)
    op.VoltageUpperThresholdCritical = get_value_to_update(op.VoltageUpperThresholdCritical,
        data.VccHighAlarmVolts, default)
    set_voltage_upper_threshold_critical_to_device_obj(op, data.VccHighAlarmVolts)
    op.VoltageLowerThresholdCritical = get_value_to_update(op.VoltageLowerThresholdCritical,
        data.VccLowAlarmVolts, default)
    set_voltage_lower_threshold_critical_to_device_obj(op, data.VccLowAlarmVolts)

    -- bmc.kepler.Systems.OpticalModule.BiasCurrent
    update_tx_bias_current_milli_amps_from_bma(op, data, default_arr)
    update_bc_tx_upper_threshold_critical_from_bma(op, data, default)
    update_bc_tx_lower_threshold_critical_from_bma(op, data, default)

    -- bmc.kepler.Systems.OpticalModule.Power
    update_tx_output_power_milli_watts_from_bma(op, data, default_arr)
    update_power_tx_upper_threshold_critical_from_bma(op, data, default)
    update_power_tx_lower_threshold_critical_from_bma(op, data, default)
    update_rx_input_power_milli_watts_from_bma(op, data, default_arr)
    update_rx_upper_threshold_critical_from_bma(op, data, default)
    update_rx_lower_threshold_critical_from_bma(op, data, default)
    op.Presence = update_presence(op.Presence, data.IsSffExist, default_num)
    set_presence_to_device_obj(op, data.IsSffExist)
end

function c_handler_optical_diag:reset()
    for object, _ in pairs(self.objects) do
        object:reset_bma_stats()
    end
end

function c_handler_optical_diag:add(_, data, object)
    self:update_optical_diag(object, data)
end

function c_handler_optical_diag:update(_, data, object)
    self:update_optical_diag(object, data)
end

return singleton(c_handler_optical_diag)
