-- 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 ncsi_capabilities_object = require 'ncsi_capabilities_object'
local ipmi = require 'ipmi'
local comp_code = ipmi.types.Cc
local log = require 'mc.logging'
local s_unpack = string.unpack
local s_pack = string.pack
local chassis_def = require 'def.chassis_def'

local ncsi_capabilities_service = {}
ncsi_capabilities_service.__index = ncsi_capabilities_service

function ncsi_capabilities_service.new()
    return setmetatable({ncsi_collection = {}}, ncsi_capabilities_service)
end

function ncsi_capabilities_service:add_object(obj)
    local chassis_obj = ncsi_capabilities_object.new_for_csr(obj)
    table.insert(self.ncsi_collection, chassis_obj)
end

function ncsi_capabilities_service:get_prop_by_id(device_id)
    for _, chassis_obj in pairs(self.ncsi_collection) do
        local ok, prop = pcall(function()
            return chassis_obj:get_prop_by_id(device_id)
        end)
        if ok and prop then
            return prop
        end
    end
    return nil
end

local function response_error(err_code)
    return err_code, 0x0007DB, 0, 0, ''
end

local function parse_devices(devices)
    local device_tb = {}
    local device_filter = {}
    local len = #devices
    for i = 1, len do
        local device = s_unpack('I1', string.sub(devices, i, i))
        if device_filter[device] or not chassis_def.DEVICEID_MAP[device] then
            return nil
        end
        device_filter[device] = 1
        device_tb[i] = device
    end
    return device_tb
end

function ncsi_capabilities_service:construct_devices_rsp(devices)
    local device_rsp = {}
    local index = 0
    for i = 1, #devices do
        local val = self:get_prop_by_id(devices[i])
        if val == nil then
            val = 0xFE
        end
        index = index + 1
        device_rsp[index] = s_pack("I1", devices[i])
        index = index + 1
        device_rsp[index] = s_pack("I1", 1)
        index = index + 1
        device_rsp[index] = s_pack("I1", val)
    end
    return table.concat(device_rsp)
end

function ncsi_capabilities_service:construct_all_device_info()
    local devices = {
        [1] = 1,
        [2] = 2,
        [3] = 5,
        [4] = 9,
        [5] = 10,
        [6] = 13,
        [7] = 14
    }
    local devices_rps = self:construct_devices_rsp(devices)
    local info_len = #devices_rps
    return comp_code.Success, 0x0007DB, 0, info_len, devices_rps
end

function ncsi_capabilities_service:GetDeviceCapabilities(req, ctx)
    local device_num = req.DeviceNum
    local offset = req.ReadOffset
    local devices = req.Devices
    if offset ~= 0 then
        log:error('[chassis]get device capabilities: offset(%u) invalid.', offset)
        return response_error(comp_code.InvalidFieldRequest)
    end

    if device_num == 0xff then
        return self:construct_all_device_info()
    end

    if devices == nil or #devices == 0 then
        log:error('[chassis]get device capabilities: devices len invalid.')
        return response_error(comp_code.ReqDataLenInvalid)
    end

    local real_device_num = #devices
    if real_device_num ~= device_num then
        log:error('[chassis]get device capabilities: device param(%u) not' ..
            ' equal real device num(%u).',
            device_num, real_device_num)
        return response_error(comp_code.InvalidFieldRequest)
    end

    local device_tb = parse_devices(devices)
    if device_tb == nil or #device_tb == 0 then
        log:error('[chassis]get device capabilities: parse devices fail.')
        return response_error(comp_code.InvalidFieldRequest)
    end

    local devices_rps = self:construct_devices_rsp(device_tb)
    local info_len = #devices_rps
    return comp_code.Success, 0x0007DB, 0, info_len, devices_rps
end

function ncsi_capabilities_service:SetDeviceCapabilities(req, ctx)
    return comp_code.DataNotAvailable, 0x0007DB
end

return ncsi_capabilities_service