-- 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 log = require 'mc.logging'
local singleton = require 'mc.singleton'
local class = require 'mc.class'
local client_base = require 'manufacture.client'
local context = require 'mc.context'

local manufacture_service = class(client_base)

local HOST_TYPE = '/bmc/kepler/Managers/1/Multihost'

function manufacture_service:ctor()
end

local function get_host_type()
    local objs = client_base:GetMultihostObjects()
    for path, obj in pairs(objs) do
        if path == HOST_TYPE then
            return obj.HostType
        end
    end
end

-- 获取 测试类型:对应path 的list
function manufacture_service:get_type_to_path_list()
    if not self.obj_list then
        return
    end
    self.type_list = {}
    for path, obj in pairs(self.obj_list) do
        if not self.type_list[obj.Type] then
            self.type_list[obj.Type] = {}
        end
        table.insert(self.type_list[obj.Type], 1, path)
    end
end

-- 获取 测试类型:dft_id 的list
function manufacture_service:get_dftid_to_path_list()
    if not self.obj_list then
        return
    end
    self.dftid_list = {}
    for path, obj in pairs(self.obj_list) do
        if not self.dftid_list[obj.Id] then
            self.dftid_list[obj.Id] = {}
        end
        table.insert(self.dftid_list[obj.Id], 1, path)
    end
end

-- 获取不同host下的测试项
function manufacture_service:insert_dft_test_path()
    self.dftid_list = {}
    self.type_list = {}
    for path, obj in pairs(self.host_type) do
        local systemid = obj.SystemId
        if not self.type_list[systemid] then
            self.type_list[systemid] = {}
        end
        if not self.obj_list[path] then
            goto continue
        end
        local type = self.obj_list[path].Type
        local id = self.obj_list[path].Id
        if not self.type_list[systemid][type] then
            self.type_list[systemid][type] = {}
        end
        if  self.type_list[systemid][type][path] == nil then
            table.insert(self.type_list[systemid][type], 1, path)
            self.type_list[systemid][type][path] = true
        end
        if not self.dftid_list[systemid] then
            self.dftid_list[systemid] = {}
        end
        if not self.dftid_list[systemid][id] then
            self.dftid_list[systemid][id] = {}
        end
        if self.dftid_list[systemid][id][path] == nil then
            table.insert(self.dftid_list[systemid][id], 1, path)
            self.dftid_list[systemid][id][path] = true
        end
        ::continue::
    end
end

-- 插入未配置SystemId的公共测试项
function manufacture_service:get_public_dft(type, HostId)
    for path, obj in pairs(self.obj_list) do
       if self.host_type[path] then
           goto continue
       end
       local type = obj.Type
       local id = obj.Id
       if not self.type_list[HostId] then
           self.type_list[HostId]= {}
       end
       if not self.type_list[HostId][type] then
           self.type_list[HostId][type] = {}
       end
       if not self.dftid_list[HostId] then
           self.dftid_list[HostId] = {}
       end
       if not self.dftid_list[HostId][id] then
           self.dftid_list[HostId][id] = {}
       end
       if self.type_list[HostId][type][path] == nil then
           table.insert(self.type_list[HostId][type], 1, path)
           self.type_list[HostId][type][path] = true
       end
       if self.dftid_list[HostId][id][path] == nil then
           table.insert(self.dftid_list[HostId][id], 1, path)
           self.dftid_list[HostId][id][path] = true
       end
       ::continue::
   end
end

-- 获取指定测试类型的对象数量
function manufacture_service:get_type_total(type, HostId)
    self.obj_list = client_base:GetManufactureObjects()
    self.host_type = client_base:GetExtensionObjects()
    if not self.obj_list or not self.host_type then
        return 0
    end
    
    if get_host_type() == 'Multihost' and HostId ~= 0 then
        self:insert_dft_test_path()
        self:get_public_dft(type, HostId)
        if not self.type_list[HostId][type] then
            log:error("this type=%d list is empty in host %d", type, HostId)
            return 0
        end
        return #self.type_list[HostId][type]
    else
        self:get_type_to_path_list()
        self:get_dftid_to_path_list()
        if not self.type_list[type] then
            log:error("this type=%d list is empty", type)
            return 0
        end
        return #self.type_list[type]
    end
end

-- 检查类型是否存在
function manufacture_service:check_type_exist(type, HostId)
    if not self.type_list then
        log:error("type_list is nil")
        return false
    end
    if get_host_type() == 'Multihost' and HostId ~= 0 then
        if self.type_list[HostId][type] then
            return true
        end
    else
        if self.type_list[type] then
            return true
        end
    end
    return false
end

-- 获取指定type中的第n个对象
function manufacture_service:get_obj_form_type_list_by_count(type, n, HostId)
    local path
    if get_host_type() == 'Multihost' and HostId ~= 0 then
        if not self.type_list[HostId][type] then
            return nil
        end
        if not self.type_list[HostId][type][n] then
            return nil
        end
        path = self.type_list[HostId][type][n]
    else
        if not self.type_list[type] then
            return nil
        end
        if not self.type_list[type][n] then
            return nil
        end
        path = self.type_list[type][n]
    end
    return self.obj_list[path]
end

-- 通过{dftid, slot, device}三元组 获取指定对象
function manufacture_service:get_obj_by_dftid(dftid, slot, device_num, HostId)
    if not self.dftid_list then
        return nil
    end
    local list
    if get_host_type() == 'Multihost' and HostId ~= 0 then
        list = self.dftid_list[HostId][dftid]
    else
        list = self.dftid_list[dftid]
    end
    if not list then
        return nil
    end
    for _, path in pairs(list) do
        local obj = self.obj_list[path]
        if not obj then
            return nil
        end
        if obj.Slot == slot and obj.DeviceNum == device_num then
            return obj
        end
    end
    return nil
end

local ACCYLE<const> = "ACCycle"
local ACCYLE_CAUSE<const> = "Unknown"
-- 调用ac掉电方法
function manufacture_service:ac()
    local _, fructrl = next(client_base:GetFruCtrlObjects())
    if not fructrl then
        log:error("get fructrl obj failed")
        return
    end
    fructrl.pcall:PowerCtrl_PACKED(context.new(), ACCYLE, ACCYLE_CAUSE)
end

local POWER_ON<const> = "On"
local POWERON_CAUSE<const> = "ChassisControlCommand"
-- 调用上电
function manufacture_service:power_on()
    local _, fructrl = next(client_base:GetFruCtrlObjects())
    if not fructrl then
        log:error("get fructrl obj failed")
        return
    end
    fructrl.pcall:PowerCtrl_PACKED(context.new(), POWER_ON, POWERON_CAUSE)
end

local FORCE_POWER_OFF<const> = "ForceOff"
local FORCE_POWER_CAUSE<const> = "ChassisControlCommand"
-- 调用强制下电
function manufacture_service:force_power_off()
    local _, fructrl = next(client_base:GetFruCtrlObjects())
    if not fructrl then
        log:error("get fructrl obj failed")
        return
    end
    fructrl.pcall:PowerCtrl_PACKED(context.new(), FORCE_POWER_OFF, FORCE_POWER_CAUSE)
end

-- 获取上下电状态
function manufacture_service:get_power_status()
    local _, fructrl = next(client_base:GetFruCtrlObjects())
    if not fructrl then
        log:error("get fructrl obj failed")
        return
    end
    return fructrl.PowerState
end

-- 调用重启方法
function manufacture_service:reboot()
    local _, systemctrl = next(client_base:GetSystemControlObjects())
    if not systemctrl then
        log:debug("get systemctrl obj list faild")
        return
    end
    -- 重启类型0:正常启动，1:最小系统
    systemctrl.pcall:GracefulReset_PACKED(context.new(), 0)
end

-- 调用清除cmos方法
function manufacture_service:clear_cmos()
    local _, bios = next(client_base:GetBiosObjects())
    if not bios then
        log:error("get bios obj list faild")
        return
    end
    return bios.pcall:ClearCmos_PACKED(context.new())
end

-- 获取FT/ST 模式
function manufacture_service:get_dft_mode()
    local _, dft_status = next(client_base:GetStatusObjects())
    if not dft_status then
        log:error("get dft obj failed")
        return
    end
    return dft_status.Mode
end

-- 获取装备enable
function manufacture_service:get_dft_enable()
    local _, dft_status = next(client_base:GetStatusObjects())
    if not dft_status then
        log:error("get dft obj failed")
        return
    end
    return dft_status.Enabled and 1 or 0
end

return singleton(manufacture_service)
