-- 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 singleton = require 'mc.singleton'
local log = require 'mc.logging'
local cjson = require 'cjson'
local c_optical_module = require 'device.class.optical_module'
local c_network_port = require 'device.class.network_port'

local metric_collect = class()

function metric_collect:ctor(bus)
    self.bus = bus
end

function metric_collect:get_metric_items(obj)
    -- 对象不在位无需触发采集
    local op = c_optical_module.collection:find(function(o)
        return o.path == obj.path
    end)
    if not op or op.Presence ~= 1 then
        return '', {}, {}, {}
    end
    local Classification = {
        {PropName = 'VendorName', PropVal = op.Manufacturer},
        {PropName = 'SerialNumber', PropVal = op.SerialNumber},
        {PropName = 'TransceiverType', PropVal = op.TransceiverType}
    }
    local location = '%s_PORT%s'
    -- 当前PortID在csr中定义，且对象上树前有校验，不存在65535的场景，故此处+1不会超过linux端口范围
    local Identification = {
        {PropName = 'Location', PropVal = string.format(location, op.NetworkAdapterId, op.PortID + 1)},
        {PropName = 'SerialNumber', PropVal = op.SerialNumber}
    }
    local MetricName = {
        'opticalmodule.metric'
    }
    return 'OpticalModule', Classification, Identification, MetricName
end

-- (OpticalModule to NetworkPort) PortID
local port_id_table = {
    [0] = {0, 2},
    [3] = {1, 3},
    [4] = {4 ,6},
    [7] = {5 ,7},
    [8] = {8 ,10},
    [11] = {9 ,11},
    [12] = {12 ,14},
    [15] = {13 ,15}
}

local function get_metric_data(obj)
    local data = {
        ['RxPowerMilliWatts'] = obj.RXInputPowerMilliWatts,
        ['TxPowerMilliWatts'] = obj.TXOutputPowerMilliWatts,
        ['BiasCurrentMilliAmps'] = obj.TXBiasCurrentMilliAmps,
        ['TemperatureCel'] = obj.TemperatureCelsius,
        ['VoltageMilliVoltage'] = obj.SupplyVoltage,
        ['RxLoss'] = obj.RxLossState,
        ['TxLoss'] = obj.TxLossState,
        ['MacRxFcsErrPktNum'] = 0,
        ['MacRxTotalPktNum'] = 0,
        ['RxLossOfLock'] = obj.RxLossOfLock,
        ['TxLossOfLock'] = obj.TxLossOfLock,
        ['LaserRuntimeSeconds'] = obj.LaserRuntimeSeconds,
        ['UptimeSeconds'] = obj.UptimeSeconds,
        ['PowerOnCount'] = obj.PowerOnCount,
        ['PowerStatus'] = obj.PowerStatus,
        ['OdspDieTemperatureCelsius'] = obj.OdspDieTemperatureCelsius,
        ['OdspHighTempRuntimeSeconds'] = obj.OdspHighTempRuntimeSeconds,
        ['LaserTemperatureCelsius'] = obj.LaserTemperatureCelsius,
        ['CorrectableFECErrors'] = obj.CorrectableFECErrors,
        ['UnCorrectableFECErrors'] = obj.UnCorrectableFECErrors,
        ['MediaSNR'] = obj.MediaSNR,
        ['HostSNR'] = obj.HostSNR,
        ['RxTotalPktNum'] = obj.RxTotalPktNum,
        ['RxBadPktNum'] = obj.RxBadPktNum
    }
    -- 错误信息统计需要整合2个光模块
    local port_objs = {}
    if port_id_table[obj.PortID] then
        port_objs = {
            c_network_port.collection:find({
                PortID = port_id_table[obj.PortID][1]
            }),
            c_network_port.collection:find({
                PortID = port_id_table[obj.PortID][2]
            })
        }
    end
    for _, port_obj in pairs(port_objs) do
        data['MacRxFcsErrPktNum'] = data['MacRxFcsErrPktNum'] + (tonumber(port_obj.RXFCSErrors) or 0)
        data['MacRxTotalPktNum'] = data['MacRxTotalPktNum'] + (tonumber(port_obj.RXFrames) or 0)
    end
    return cjson.encode(data)
end

function metric_collect:get_metric_data(obj, metric_name)
    local op = c_optical_module.collection:find(function(o)
        return o.path == obj.path
    end)
    if not op then
        return {}
    end
    local val = {}
    for _, value in ipairs(metric_name) do
        if value == 'opticalmodule.metric' then
            local data = get_metric_data(op)
            if #data ~= 0 then
                table.insert(val, {MetricName = value, Data = {data}})
            end
        else
            log:notice('invalid metric, metric_name = %s', metric_name)
        end
    end
    return val
end

return singleton(metric_collect)