-- 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 skynet = require 'skynet'
local log = require 'mc.logging'
local mdb = require 'mc.mdb'
local ctx = require 'mc.context'
require 'skynet.manager'

local sensor_common = {}
sensor_common.__index = sensor_common

function sensor_common.wait_test_service_ok(bus, name)
    -- 由于集成测试客户端启动时，DBUS上只有service name，无法使用ping
    local org_freedesktop_dbus = require 'sd_bus.org_freedesktop_dbus'
    for i = 1, 10 do
        local owner = org_freedesktop_dbus.get_name_owner(bus, name)
        if owner then
            log:info('== wait test service %s start ok, costs %d', name, i)
            return
        end
        skynet.sleep(30)
    end
    local msg = string.format('== wait test service %s start failed', name)
    log:error(msg)
    error(msg)
end

function sensor_common.wait_resource(bus, name, path)
    local org_freedesktop_dbus = require 'sd_bus.org_freedesktop_dbus'
    for i = 1, 10 do
        local err = org_freedesktop_dbus.ping(bus, name, path)
        if not err then
            log:info('== wait resource (%s:%s) start ok, costs %d seconds', name, path, i - 1)
            return
        end
        skynet.sleep(100)
    end
    log:warn('== wait resource (%s:%s) timeout', name, path)
end

function sensor_common.wait_interface(bus, path, intf)
    for i = 1, 10 do
        local ret, _ = pcall(mdb.get_object, bus, path, intf)
        if ret then
            log:info('== wait display resource cost %d seconds.', i - 1)
            return
        end
        skynet.sleep(100)
    end
    log:warn('== wait display resource (%s:%s) timeout', path, intf)
end

function sensor_common.start_test_service(bus, path, name)
    local res = skynet.newservice(path)
    sensor_common.wait_test_service_ok(bus, name)
    return res
end

function sensor_common.set_log_level(bus, apps, level)
    for _, app in ipairs(apps) do
        local service = string.format('bmc.kepler.%s', app)
        local path = string.format('/bmc/kepler/%s/MicroComponent', app)
        local intf = 'bmc.kepler.MicroComponent.Debug'
        local ok, msg = pcall(bus.call, bus, service, path, intf, 'SetDlogLevel', 'a{ss}sy', ctx.new(), level, 0)
        if not ok then
            log:error('Set app [%s] log level [%s] failed, error: %s', app, level, msg)
        else
            log:notice('Set app [%s] log level [%s] successfully.', app, level)
        end
    end
end

function sensor_common.split(s, reps)
    local result = {}
    string.gsub(s, '[^' .. reps .. ']+', function (ss)
        table.insert(result, ss)
    end)
    return result
end

function sensor_common.convert_sel_data(data)
    local bs = require 'mc.bitstring'
    local utils = require 'sensor_utils'

    local r = {}
    r[#r+1] = utils.toc(data.sensor_type, utils.BITS_8, true)
    r[#r+1] = utils.toc(data.sensor_no, utils.BITS_8, true)
    local event_type = string.unpack('I1', bs.new([[<<et:7,ed:1>>]]):pack({et = data.event_type, ed = data.event_dir}))
    r[#r+1] = utils.toc(event_type, utils.BITS_8, true)
    r[#r+1] = utils.toc(data.data1, utils.BITS_8, true)
    r[#r+1] = utils.toc(data.data2, utils.BITS_8, true)
    r[#r+1] = utils.toc(data.data3, utils.BITS_8, true)
    return table.concat(r, '')
end

function sensor_common.call_ipmi(bus, netfn, cmd, payload, host)
    local json = require 'cjson'
    local bs = require 'mc.bitstring'
    local enums = require 'ipmi.enums'

    local service = 'bmc.kepler.ipmi_core'
    local path = '/bmc/kepler/IpmiCore'
    local intf = 'bmc.kepler.IpmiCore'
    local ipmi_ctx = json.encode({
        ChanType = host and enums.ChannelType.CT_HOST:value() or enums.ChannelType.CT_ME:value(),
        HostId = host,
        Instance = 0,
        dest_lun = 0
    })
    local ipmi_req = bs.new('<<_,_:2,DestNetFn:6,_:3/unit:8,Cmd,Payload/string>>'):pack({
        DestNetFn = netfn,
        Cmd = cmd,
        Payload = payload and payload .. '\x00' or '\x00'  -- 添加校验位
    })
    return pcall(bus.call, bus, service, path, intf, 'Route', 'a{ss}ayay', ctx.new(), ipmi_req, ipmi_ctx)
end

function sensor_common.get_mdb_property(bus, service, path, intf, prop)
    local member = 'org.freedesktop.DBus.Properties'
    return bus:call(service, path, member, 'Get', 'ss', intf, prop):value()
end

function sensor_common.check_with_delay(func)
    -- 事件生成 E2E 是 100ms，以这个规格进行delay测试
    -- 最大等待 3 秒钟，并且打印对应的性能日志，作为性能测试参考，同时适用于其他流程
    local ok, err
    for i = 1, 300 do
        ok, err = pcall(func)
        if ok then
            log:notice('check is passed in [%s] ms', i * 10)
            return true
        end
        skynet.sleep(1)
    end
    log:error('check_with_delay failed: %s', err)
    return false
end

-- 格式化查询的sel，便于直接通过Key拿到属性
function sensor_common.format_sel(list)
    if not list or #list == 0  then
        return {}
    end

    local res = {}
    local key_head = list[1].MappingTable[1].Key
    local key, value, sel
    for _, v in ipairs(list) do
        key = v.MappingTable[1].Key
        value = v.MappingTable[1].Value
        if key == key_head then
            res[#res + 1] = {}
            sel = res[#res]
        end

        sel[key] = value
    end
    return res
end

return sensor_common
