-- 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 msg = require 'general_hardware.ipmi.ipmi_message'
local log = require 'mc.logging'
local ipmi = require 'ipmi'
local json = require 'cjson'
local unit_manager = require 'unit_manager.unit_manager'
local cc = ipmi.types.Cc

local SYS_INFO_BLOCK_LEN <const> = 16
local MAX_SYS_INFO_BLOCK <const> = 16
local MANUFACTURE_ID<const> = 0x0007db

local common_ipmi = {}
common_ipmi.__index = common_ipmi

function common_ipmi.new()
    return setmetatable({}, common_ipmi)
end

function common_ipmi:init(db)
    self.db = db
end

local function init_system_info_list()
    local init_arry = {}
    for _ = 1, SYS_INFO_BLOCK_LEN * MAX_SYS_INFO_BLOCK do
        table.insert(init_arry, "")
    end
    return init_arry
end

local function get_system_info_arry(system_info)
    if not system_info then
        return init_system_info_list()
    else
        return json.decode(system_info)
    end
end

function common_ipmi:set_system_info_parameters(req, ctx)
    local system_info_arry = {}
    local SystemInfoList = self.db.SystemInfoList({
        Id = 'system_info_list'
    })
    system_info_arry = get_system_info_arry(SystemInfoList.SystemInfo)
    local data = {}
    for i = 1, #req.Data do
        table.insert(data, string.byte(req.Data, i))
    end
    if not data[1] or data[1] < 0 or data[1] >=  MAX_SYS_INFO_BLOCK then
        log:error('[Common IPMI] request Data(%s) is invaild', req.SetSelector)
        ipmi.ipmi_operation_log(ctx, 'general_hardware', 'Set system information failed')
        return msg.SetSystemInfoParametersRsp.new(cc.InvalidFieldRequest)
    end
    local offset = SYS_INFO_BLOCK_LEN * (data[1])
    for i = 2, #data do
        system_info_arry[offset + i - 1] = data[i]
    end
    -- 清除超出长度的值
    local len = system_info_arry[1]
    if type(len) ~= 'number' then
        log:error('[Common IPMI]  SystemInfo len is invaild')
        ipmi.ipmi_operation_log(ctx, 'general_hardware', 'Set system information failed')
        return msg.SetSystemInfoParametersRsp.new(cc.DataNotAvailable)
    end
    for i = len + 2, #system_info_arry do
        system_info_arry[i] = ""
    end
    local system_info = json.encode(system_info_arry)
    SystemInfoList.SystemInfo = system_info
    SystemInfoList:save()
    ipmi.ipmi_operation_log(ctx, 'general_hardware', 'Set system information successfully')
    return msg.SetSystemInfoParametersRsp.new(cc.Success)
end

function common_ipmi:get_system_info_parameters(req, ctx)
    local SystemInfoList = self.db.SystemInfoList({
        Id = 'system_info_list'
    })
    if not SystemInfoList.SystemInfo then
        log:error('[Common IPMI] SystemInfo is nil')
        return msg.GetSystemInfoParametersRsp.new(cc.DataNotAvailable, 0, 0)
    end
    local system_info = SystemInfoList.SystemInfo
    local system_info_arry = json.decode(system_info)

    if req.SetSelector < 0 or req.SetSelector >= MAX_SYS_INFO_BLOCK then
        log:error('[Common IPMI] request SetSelector(%s) is invaild', req.SetSelector)
        return msg.GetSystemInfoParametersRsp.new(cc.InvalidFieldRequest, 0, 0)
    end
    local offset = req.SetSelector * SYS_INFO_BLOCK_LEN
    local len = system_info_arry[1]
    if type(len) ~= 'number' or offset > len then
        log:error('[Common IPMI] SystemInfo len(%s) is invaild, SetSelector: %s ', len, req.SetSelector)
        return msg.SetSystemInfoParametersRsp.new(cc.DataNotAvailable, 0, 0)
    end
    local resp_data = ''
    local resp_len = len - offset + 1 < SYS_INFO_BLOCK_LEN and len - offset + 1 or SYS_INFO_BLOCK_LEN
    for i = 1, resp_len do
        if system_info_arry[i + offset] == "" then
            break
        end
        resp_data = resp_data .. string.char(system_info_arry[i + offset])
    end
    return msg.GetSystemInfoParametersRsp.new(cc.Success, req.SetSelector, resp_data)
end

function common_ipmi:set_device_action(req, ctx)
    local slot = req.Slot
    local npu_board = unit_manager.get_instance():get_npu_by_slot(slot)
    if not npu_board then
        log:error('[Common IPMI] set device action fail, slot(%s) invalid', slot)
        return msg.SetDeviceActionRsp.new(cc.InvalidFieldRequest, MANUFACTURE_ID)
    end
    local ok, res = pcall(function()
        npu_board:set_prop('Reset', 0x0)
    end)
    if not ok then
        log:error('[Common IPMI] set device action fail, reset fail, err %s', res )
        ipmi.ipmi_operation_log(ctx, 'general_hardware', 'Reset all chips of NPU board %s failed', slot)
        return msg.SetDeviceActionRsp.new(cc.DataNotAvailable, MANUFACTURE_ID)
    end
    ipmi.ipmi_operation_log(ctx, 'general_hardware', 'Reset all chips of NPU board %s successfully', slot)
    return msg.SetDeviceActionRsp.new(cc.Success, MANUFACTURE_ID)
end

return common_ipmi