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

local OverviewTypes = require 'frudata.json_types.Overview'
local ChassisTypes = require 'frudata.json_types.Chassis'
local BoardTypes = require 'frudata.json_types.Board'
local ProductTypes = require 'frudata.json_types.Product'
local SystemTypes = require 'frudata.json_types.System'
local FrudataTypes = require 'frudata.json_types.Frudata'
local PropertiesTypes = require 'mdb.bmc.kepler.Object.PropertiesInterface'
local ComponentTypes = require 'frudata.json_types.Component'
local ComponentsTypes = require 'frudata.json_types.Components'
local FrusTypes = require 'frudata.json_types.Frus'
local FruTypes = require 'frudata.json_types.Fru'
local ManufactureTypes = require 'frudata.json_types.Manufacture'
local ExtensionTypes = require 'frudata.json_types.Extension'
local FruDatasTypes = require 'frudata.json_types.FruDatas'

local FruData = mdb.register_object('/bmc/kepler/Systems/:SystemId/FruDatas/:Id', {
    {name = 'bmc.kepler.Systems.FruData.Overview', interface = OverviewTypes.interface},
    {name = 'bmc.kepler.Systems.FruData.Chassis', interface = ChassisTypes.interface},
    {name = 'bmc.kepler.Systems.FruData.Board', interface = BoardTypes.interface},
    {name = 'bmc.kepler.Systems.FruData.Product', interface = ProductTypes.interface},
    {name = 'bmc.kepler.Systems.FruData.System', interface = SystemTypes.interface},
    {name = 'bmc.kepler.FrudataService.Frudata', interface = FrudataTypes.interface},
    {name = 'bmc.kepler.Object.Properties', interface = PropertiesTypes.interface}
})

function FruData:ctor(SystemId, Id)
    self.path = '/bmc/kepler/Systems/' .. SystemId .. '/FruDatas/' .. Id
end

local Component = mdb.register_object('/bmc/kepler/Systems/:SystemId/Components/:Id', {
    {name = 'bmc.kepler.Systems.Component', interface = ComponentTypes.interface},
    {name = 'bmc.kepler.Object.Properties', interface = PropertiesTypes.interface}
})

function Component:ctor(SystemId, Id)
    self.path = '/bmc/kepler/Systems/' .. SystemId .. '/Components/' .. Id
end

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

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

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

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

local Fru = mdb.register_object('/bmc/kepler/Systems/:SystemId/Frus/:Id', {
    {name = 'bmc.kepler.Systems.Fru', interface = FruTypes.interface},
    {name = 'bmc.kepler.Object.Properties', interface = PropertiesTypes.interface}
})

function Fru:ctor(SystemId, Id)
    self.path = '/bmc/kepler/Systems/' .. SystemId .. '/Frus/' .. Id
end

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

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

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

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

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

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

local model = require 'class.model'

local frudata_service = class(app_base.Service)

frudata_service.package = 'frudata'

function frudata_service:CreateFruData(SystemId, Id, prop_setting_cb)
    local path = '/bmc/kepler/Systems/' .. SystemId .. '/FruDatas/' .. Id
    return object_manage.create_object('FruData', path, path, prop_setting_cb)
end

function frudata_service:CreateComponent(SystemId, Id, prop_setting_cb)
    local path = '/bmc/kepler/Systems/' .. SystemId .. '/Components/' .. Id
    return object_manage.create_object('Component', path, path, prop_setting_cb)
end

function frudata_service:CreateComponents(SystemId, prop_setting_cb)
    local path = '/bmc/kepler/Systems/' .. SystemId .. '/Components'
    return object_manage.create_object('Components', path, path, prop_setting_cb)
end

function frudata_service:CreateFrus(SystemId, prop_setting_cb)
    local path = '/bmc/kepler/Systems/' .. SystemId .. '/Frus'
    return object_manage.create_object('Frus', path, path, prop_setting_cb)
end

function frudata_service:CreateFru(SystemId, Id, prop_setting_cb)
    local path = '/bmc/kepler/Systems/' .. SystemId .. '/Frus/' .. Id
    return object_manage.create_object('Fru', path, path, prop_setting_cb)
end

function frudata_service:CreateDftEeprom(Id, prop_setting_cb)
    local path = '/bmc/kepler/Manufacture/SelfTest/DftEeprom/' .. Id
    return object_manage.create_object('DftEeprom', path, path, prop_setting_cb)
end

function frudata_service:CreateDftEepromWp(Id, prop_setting_cb)
    local path = '/bmc/kepler/Manufacture/SelfTest/DftEepromWp/' .. Id
    return object_manage.create_object('DftEepromWp', path, path, prop_setting_cb)
end

function frudata_service:CreateFruDatas(ChassisId, prop_setting_cb)
    local path = '/bmc/kepler/Chassis/' .. ChassisId .. '/FruDatas'
    return object_manage.create_object('FruDatas', path, path, prop_setting_cb)
end

function frudata_service:ImplFruDataFrudataSetProductAssetTag(cb)
    model.ImplFruDataFrudataSetProductAssetTag(cb)
end

function frudata_service:ImplFruDataFrudataSetSysProductName(cb)
    model.ImplFruDataFrudataSetSysProductName(cb)
end

function frudata_service:ImplFruDataFrudataUpdate(cb)
    model.ImplFruDataFrudataUpdate(cb)
end

function frudata_service:ImplComponentComponentUpdateHealth(cb)
    model.ImplComponentComponentUpdateHealth(cb)
end

function frudata_service:ImplComponentsComponentsGetComponentTypes(cb)
    model.ImplComponentsComponentsGetComponentTypes(cb)
end

function frudata_service:ImplDftEepromManufactureStart(cb)
    model.ImplDftEepromManufactureStart(cb)
end

function frudata_service:ImplDftEepromManufactureStop(cb)
    model.ImplDftEepromManufactureStop(cb)
end

function frudata_service:ImplDftEepromManufactureGetResult(cb)
    model.ImplDftEepromManufactureGetResult(cb)
end

function frudata_service:ImplDftEepromWpManufactureStart(cb)
    model.ImplDftEepromWpManufactureStart(cb)
end

function frudata_service:ImplDftEepromWpManufactureStop(cb)
    model.ImplDftEepromWpManufactureStop(cb)
end

function frudata_service:ImplDftEepromWpManufactureGetResult(cb)
    model.ImplDftEepromWpManufactureGetResult(cb)
end

function frudata_service:ImplFruDatasFruDatasGetFruDataList(cb)
    model.ImplFruDatasFruDatasGetFruDataList(cb)
end

---@param mdb_object object
---@param FruId integer
---@param FruName string
---@param Path string
function frudata_service:FrusFrusFruAdded(mdb_object, FruId, FruName, Path)
    self.bus:signal(mdb_object.path, 'bmc.kepler.Systems.Frus', 'FruAdded', 'a{ss}yss', context.get_context() or {},
        FruId, FruName, Path)
end

---@param mdb_object object
---@param FruId integer
---@param FruName string
---@param Path string
function frudata_service:FrusFrusFruRemoved(mdb_object, FruId, FruName, Path)
    self.bus:signal(mdb_object.path, 'bmc.kepler.Systems.Frus', 'FruRemoved', 'a{ss}yss', context.get_context() or {},
        FruId, FruName, Path)
end

function frudata_service:get_bus()
    return self.bus
end

function frudata_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 frudata_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 frudata_service:ctor()
    self.ipmi_cmds = {}
    self.signal_slots = {}
    self.name = self.name or frudata_service.package
    self.db = open_db(':memory:', datas)
    self.local_db = open_local_db(app_base.Service:get_local_db_path(self.name) .. '/frudata.db', datas, 'poweroff')

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

function frudata_service:pre_init()
    frudata_service.super.pre_init(self)
    self.persist = persist_client.new(self.bus, self.db, self, {})
    object_manage.set_persist_client(self.persist)
end

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

return frudata_service
