-- 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 ipmi = require 'ipmi'
local mdb = require 'mc.mdb'
local class = require 'mc.class'
local context = require 'mc.context'
local open_db = require 'power_mgmt.db'
local app_base = require 'mc.service_app_base'
local open_local_db = require 'power_mgmt.local_db'
local object_manage = require 'mc.mdb.object_manage'
local persist_client = require 'persistence.persist_client_lib'
local orm_classes = require 'power_mgmt.orm_classes'
local ok, datas = pcall(require, 'power_mgmt.datas')
if not ok then
    datas = nil -- 如果没有datas配置，证明当前组件不需要datas，仅打开数据库
end

local PowerSuppliesTypes = require 'power_mgmt.json_types.PowerSupplies'
local PropertiesTypes = require 'power_mgmt.json_types.Properties'
local PowerMgmtTypes = require 'power_mgmt.json_types.PowerMgmt'
local OnePowerTypes = require 'power_mgmt.json_types.OnePower'
local OnePowerMetricsTypes = require 'power_mgmt.json_types.OnePowerMetrics'
local OutputControlTypes = require 'power_mgmt.json_types.OutputControl'
local TemperaturesTypes = require 'power_mgmt.json_types.Temperatures'
local StatusTypes = require 'power_mgmt.json_types.Status'
local EfficiencyCurveTypes = require 'power_mgmt.json_types.EfficiencyCurve'
local MetricTypes = require 'power_mgmt.json_types.Metric'
local CollectorTypes = require 'power_mgmt.json_types.Collector'
local CapacitorTypes = require 'power_mgmt.json_types.Capacitor'
local CapacitorMetricsTypes = require 'power_mgmt.json_types.CapacitorMetrics'
local BlackBoxTypes = require 'power_mgmt.json_types.BlackBox'
local ManufactureTypes = require 'power_mgmt.json_types.Manufacture'

local PowerSupplies = mdb.register_object('/bmc/kepler/Chassis/:ChassisId/PowerSubsystem/PowerSupplies', {
    {name = 'bmc.kepler.Chassis.PowerSubsystem.PowerSupplies', interface = PowerSuppliesTypes.interface},
    {name = 'bmc.kepler.Object.Properties', interface = PropertiesTypes.interface}
})

function PowerSupplies:ctor(ChassisId)
    self.path = '/bmc/kepler/Chassis/' .. ChassisId .. '/PowerSubsystem/PowerSupplies'
end

local PowerMgmt = mdb.register_object('/bmc/kepler/Systems/:SystemId/PowerMgmt', {
    {name = 'bmc.kepler.Systems.PowerMgmt', interface = PowerMgmtTypes.interface},
    {name = 'bmc.kepler.Object.Properties', interface = PropertiesTypes.interface}
})

function PowerMgmt:ctor(SystemId)
    self.path = '/bmc/kepler/Systems/' .. SystemId .. '/PowerMgmt'
end

local OnePower = mdb.register_object('/bmc/kepler/Systems/:SystemId/PowerMgmt/:ID', {
    {name = 'bmc.kepler.Systems.PowerMgmt.OnePower', interface = OnePowerTypes.interface},
    {name = 'bmc.kepler.Systems.PowerMgmt.OnePower.Metrics', interface = OnePowerMetricsTypes.interface},
    {name = 'bmc.kepler.Chassis.PowerSubsystem.PowerSupply.OutputControl', interface = OutputControlTypes.interface},
    {name = 'bmc.kepler.Systems.PowerMgmt.OnePower.Temperatures', interface = TemperaturesTypes.interface},
    {name = 'bmc.kepler.Systems.PowerMgmt.OnePower.Status', interface = StatusTypes.interface},
    {name = 'bmc.kepler.Systems.PowerMgmt.OnePower.EfficiencyCurve', interface = EfficiencyCurveTypes.interface},
    {name = 'bmc.kepler.Metric', interface = MetricTypes.interface},
    {name = 'bmc.kepler.Release.OnePower.Collector', interface = CollectorTypes.interface},
    {name = 'bmc.kepler.Chassis.PowerSubsystem.Capacitor', interface = CapacitorTypes.interface},
    {name = 'bmc.kepler.Chassis.PowerSubsystem.Capacitor.Metrics', interface = CapacitorMetricsTypes.interface},
    {name = 'bmc.kepler.Object.Properties', interface = PropertiesTypes.interface}
})

function OnePower:ctor(SystemId, ID)
    self.path = '/bmc/kepler/Systems/' .. SystemId .. '/PowerMgmt/' .. ID
end

local PowerConverter = mdb.register_object('/bmc/kepler/Systems/:SystemId/PowerConverter/:ID', {
    {name = 'bmc.kepler.Systems.PowerMgmt.OnePower', interface = OnePowerTypes.interface},
    {name = 'bmc.kepler.Systems.PowerMgmt.OnePower.Metrics', interface = OnePowerMetricsTypes.interface},
    {name = 'bmc.kepler.Systems.PowerMgmt.OnePower.Temperatures', interface = TemperaturesTypes.interface},
    {name = 'bmc.kepler.Systems.PowerMgmt.OnePower.Status', interface = StatusTypes.interface},
    {name = 'bmc.kepler.Metric', interface = MetricTypes.interface},
    {name = 'bmc.kepler.Debug.BlackBox', interface = BlackBoxTypes.interface},
    {name = 'bmc.kepler.Object.Properties', interface = PropertiesTypes.interface}
})

function PowerConverter:ctor(SystemId, ID)
    self.path = '/bmc/kepler/Systems/' .. SystemId .. '/PowerConverter/' .. ID
end

local DftPowerSupply = mdb.register_object('/bmc/kepler/Manufacture/SelfTest/DftPowerSupply/:Id', {
    {name = 'bmc.kepler.Manufacture', interface = ManufactureTypes.interface},
    {name = 'bmc.kepler.Object.Properties', interface = PropertiesTypes.interface}
})

function DftPowerSupply:ctor(Id)
    self.path = '/bmc/kepler/Manufacture/SelfTest/DftPowerSupply/' .. Id
end

local DftCanChannel = mdb.register_object('/bmc/kepler/Manufacture/InteractTest/DftCanChannel/:Id', {
    {name = 'bmc.kepler.Manufacture', interface = ManufactureTypes.interface},
    {name = 'bmc.kepler.Object.Properties', interface = PropertiesTypes.interface}
})

function DftCanChannel:ctor(Id)
    self.path = '/bmc/kepler/Manufacture/InteractTest/DftCanChannel/' .. Id
end

local model = require 'class.model'

local power_mgmt_service = class(app_base.Service)

power_mgmt_service.package = 'power_mgmt'

function power_mgmt_service:CreatePowerSupplies(ChassisId, prop_setting_cb)
    local path = '/bmc/kepler/Chassis/' .. ChassisId .. '/PowerSubsystem/PowerSupplies'
    return object_manage.create_object('PowerSupplies', path, path, prop_setting_cb)
end

function power_mgmt_service:CreatePowerMgmt(SystemId, prop_setting_cb)
    local path = '/bmc/kepler/Systems/' .. SystemId .. '/PowerMgmt'
    return object_manage.create_object('PowerMgmt', path, path, prop_setting_cb)
end

function power_mgmt_service:CreateOnePower(SystemId, ID, prop_setting_cb)
    local path = '/bmc/kepler/Systems/' .. SystemId .. '/PowerMgmt/' .. ID
    return object_manage.create_object('OnePower', path, path, prop_setting_cb)
end

function power_mgmt_service:CreatePowerConverter(SystemId, ID, prop_setting_cb)
    local path = '/bmc/kepler/Systems/' .. SystemId .. '/PowerConverter/' .. ID
    return object_manage.create_object('PowerConverter', path, path, prop_setting_cb)
end

function power_mgmt_service:CreateDftPowerSupply(Id, prop_setting_cb)
    local path = '/bmc/kepler/Manufacture/SelfTest/DftPowerSupply/' .. Id
    return object_manage.create_object('DftPowerSupply', path, path, prop_setting_cb)
end

function power_mgmt_service:CreateDftCanChannel(Id, prop_setting_cb)
    local path = '/bmc/kepler/Manufacture/InteractTest/DftCanChannel/' .. Id
    return object_manage.create_object('DftCanChannel', path, path, prop_setting_cb)
end

function power_mgmt_service:ImplPowerSuppliesPowerSuppliesSetPsusFanMinPWM(cb)
    model.ImplPowerSuppliesPowerSuppliesSetPsusFanMinPWM(cb)
end

function power_mgmt_service:ImplOnePowerOnePowerSetPowerWorkMode(cb)
    model.ImplOnePowerOnePowerSetPowerWorkMode(cb)
end

function power_mgmt_service:ImplOnePowerOnePowerReset(cb)
    model.ImplOnePowerOnePowerReset(cb)
end

function power_mgmt_service:ImplOnePowerOnePowerSetPowerSupplyCircuit(cb)
    model.ImplOnePowerOnePowerSetPowerSupplyCircuit(cb)
end

function power_mgmt_service:ImplOnePowerOutputControlSetOutputPowerLimitWatts(cb)
    model.ImplOnePowerOutputControlSetOutputPowerLimitWatts(cb)
end

function power_mgmt_service:ImplOnePowerStatusSetSleepMode(cb)
    model.ImplOnePowerStatusSetSleepMode(cb)
end

function power_mgmt_service:ImplOnePowerStatusSetAlarmLatch(cb)
    model.ImplOnePowerStatusSetAlarmLatch(cb)
end

function power_mgmt_service:ImplOnePowerMetricGetData(cb)
    model.ImplOnePowerMetricGetData(cb)
end

function power_mgmt_service:ImplOnePowerMetricGetItems(cb)
    model.ImplOnePowerMetricGetItems(cb)
end

function power_mgmt_service:ImplOnePowerMetricSynDataAcquisitionEnable(cb)
    model.ImplOnePowerMetricSynDataAcquisitionEnable(cb)
end

function power_mgmt_service:ImplOnePowerCollectorGetFanSpeed(cb)
    model.ImplOnePowerCollectorGetFanSpeed(cb)
end

function power_mgmt_service:ImplOnePowerCollectorGetRegisterValue(cb)
    model.ImplOnePowerCollectorGetRegisterValue(cb)
end

function power_mgmt_service:ImplOnePowerCapacitorReset(cb)
    model.ImplOnePowerCapacitorReset(cb)
end

function power_mgmt_service:ImplOnePowerCapacitorSetVOUTDebounceMilliseconds(cb)
    model.ImplOnePowerCapacitorSetVOUTDebounceMilliseconds(cb)
end

function power_mgmt_service:ImplOnePowerCapacitorSetDepthOfDischargeVolts(cb)
    model.ImplOnePowerCapacitorSetDepthOfDischargeVolts(cb)
end

function power_mgmt_service:ImplPowerConverterOnePowerSetPowerWorkMode(cb)
    model.ImplPowerConverterOnePowerSetPowerWorkMode(cb)
end

function power_mgmt_service:ImplPowerConverterOnePowerReset(cb)
    model.ImplPowerConverterOnePowerReset(cb)
end

function power_mgmt_service:ImplPowerConverterOnePowerSetPowerSupplyCircuit(cb)
    model.ImplPowerConverterOnePowerSetPowerSupplyCircuit(cb)
end

function power_mgmt_service:ImplPowerConverterStatusSetSleepMode(cb)
    model.ImplPowerConverterStatusSetSleepMode(cb)
end

function power_mgmt_service:ImplPowerConverterStatusSetAlarmLatch(cb)
    model.ImplPowerConverterStatusSetAlarmLatch(cb)
end

function power_mgmt_service:ImplPowerConverterMetricGetData(cb)
    model.ImplPowerConverterMetricGetData(cb)
end

function power_mgmt_service:ImplPowerConverterMetricGetItems(cb)
    model.ImplPowerConverterMetricGetItems(cb)
end

function power_mgmt_service:ImplPowerConverterMetricSynDataAcquisitionEnable(cb)
    model.ImplPowerConverterMetricSynDataAcquisitionEnable(cb)
end

function power_mgmt_service:ImplPowerConverterBlackBoxDumpPsuBlackbox(cb)
    model.ImplPowerConverterBlackBoxDumpPsuBlackbox(cb)
end

function power_mgmt_service:ImplDftPowerSupplyManufactureStart(cb)
    model.ImplDftPowerSupplyManufactureStart(cb)
end

function power_mgmt_service:ImplDftPowerSupplyManufactureStop(cb)
    model.ImplDftPowerSupplyManufactureStop(cb)
end

function power_mgmt_service:ImplDftPowerSupplyManufactureGetResult(cb)
    model.ImplDftPowerSupplyManufactureGetResult(cb)
end

function power_mgmt_service:ImplDftCanChannelManufactureStart(cb)
    model.ImplDftCanChannelManufactureStart(cb)
end

function power_mgmt_service:ImplDftCanChannelManufactureStop(cb)
    model.ImplDftCanChannelManufactureStop(cb)
end

function power_mgmt_service:ImplDftCanChannelManufactureGetResult(cb)
    model.ImplDftCanChannelManufactureGetResult(cb)
end

---@param mdb_object object
---@param MetricName string
---@param TagPath string
function power_mgmt_service:OnePowerMetricCollectSignal(mdb_object, MetricName, TagPath)
    self.bus:signal(mdb_object.path, 'bmc.kepler.Metric', 'CollectSignal', 'a{ss}ss', context.get_context() or {},
        MetricName, TagPath)
end

---@param mdb_object object
---@param MetricName string
---@param TagPath string
function power_mgmt_service:PowerConverterMetricCollectSignal(mdb_object, MetricName, TagPath)
    self.bus:signal(mdb_object.path, 'bmc.kepler.Metric', 'CollectSignal', 'a{ss}ss', context.get_context() or {},
        MetricName, TagPath)
end

function power_mgmt_service:get_bus()
    return self.bus
end

function power_mgmt_service:register_ipmi_cmd(ipmi_cmd, cb)
    self.ipmi_cmds[ipmi_cmd.name] = ipmi.register_ipmi_cmd(self.bus, self.service_name, ipmi_cmd,
        cb or self[ipmi_cmd.name])
end

function power_mgmt_service:unregister_ipmi_cmd(ipmi_cmd)
    local cmd_obj = self.ipmi_cmds[ipmi_cmd.name]
    if not cmd_obj then
        return
    end

    cmd_obj:unregister()
    self.ipmi_cmds[ipmi_cmd.name] = nil
end

function power_mgmt_service:ctor()
    self.ipmi_cmds = {}
    self.signal_slots = {}
    self.name = self.name or power_mgmt_service.package
    self.db = open_db(':memory:', datas)
    self.local_db = open_local_db(app_base.Service:get_local_db_path(self.name) .. '/power_mgmt.db', datas, 'poweroff')

    orm_classes.init(self.db)
    self.bus:request_name(app_base.Service.get_service_name(self.name))
    model.init(self.bus)
    power_mgmt_service.bus = self.bus
end

function power_mgmt_service:pre_init()
    power_mgmt_service.super.pre_init(self)
    self.persist = persist_client.new(self.bus, self.db, self, {
        ['t_power_supplies'] = true,
        ['t_onepower'] = true,
        ['t_psu_slot'] = true,
        ['t_power_configuration'] = true,
        ['t_power_efficiency_curve'] = true
    })
    object_manage.set_persist_client(self.persist)
end

function power_mgmt_service:init()
    power_mgmt_service.super.init(self)
end

return power_mgmt_service
