-- 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 singleton = require 'mc.singleton'
local c_super_pod_label_object = require 'super_pod_label_object'
local class = require 'mc.class'
local c_object = require 'mc.orm.object'
local custom_messages = require 'messages.custom'
local log = require 'mc.logging'
local ipmi_msg = require 'chassis.ipmi.ipmi_message'
local ipmi = require 'mc.ipmi'
local cc = ipmi.types.Cc
local bs = require 'mc.bitstring'
local client = require 'chassis.client'
local MIN_SUPERPOD_SIZE<const> = 1
local MAX_SUPERPOD_SIZE<const> = 384
local SUPERPOD_LABEL_ENABLED<const> = 0x01
local SUPERPOD_LABEL_DISABLED<const> = 0x00

local c_super_pod_label_collection = class()

function c_super_pod_label_collection:ctor()
end

function c_super_pod_label_collection:SetSuperPodId(ctx, SuperPodId)
    local super_pod_label_object = c_object('SuperPodLabel').collection:find({Id = 1})
    if not super_pod_label_object then
        log:operation(ctx:get_initiator(), 'Chassis', 'Set SuperPodId to (%d) failed', SuperPodId)
        error(custom_messages.ResourceNotFound('SuperPodLabel'))
    end
    local res = super_pod_label_object:update_super_pod_id(SuperPodId)
    if not res then
        log:operation(ctx:get_initiator(), 'Chassis', 'Set SuperPodId to (%d) failed', SuperPodId)
        error(custom_messages.ResourceNotFound('SuperPodId'))
    end
    log:operation(ctx:get_initiator(), 'Chassis', 'Set SuperPodId to (%d) successfully', SuperPodId)
    return true
end

function c_super_pod_label_collection:SetServerIndex(ctx, ServerIndex)
    local super_pod_label_object = c_object('SuperPodLabel').collection:find({Id = 1})
    if not super_pod_label_object then
        log:operation(ctx:get_initiator(), 'Chassis', 'Set ServerIndex to (%d) failed', ServerIndex)
        error(custom_messages.ResourceNotFound('SuperPodLabel'))
    end
    local res = super_pod_label_object:update_server_index(ServerIndex)
    if not res then
        log:operation(ctx:get_initiator(), 'Chassis', 'Set ServerIndex to (%d) failed', ServerIndex)
        error(custom_messages.ResourceNotFound('ServerIndex'))
    end
    log:operation(ctx:get_initiator(), 'Chassis', 'Set ServerIndex to (%d) successfully', ServerIndex)
    return true
end

function c_super_pod_label_collection:SetSuperPodSize(ctx, SuperPodSize)
    local super_pod_label_object = c_super_pod_label_object.collection:find({Id = 1})
    if not super_pod_label_object then
        log:operation(ctx:get_initiator(), 'Chassis', 'Set SuperPodSize to (%d) failed', SuperPodSize)
        error(custom_messages.ResourceNotFound('SuperPodLabel'))
    end

    local res = super_pod_label_object:update_super_pod_size(SuperPodSize)
    if not res then
        log:operation(ctx:get_initiator(), 'Chassis', 'Set SuperPodSize to (%d) failed', SuperPodSize)
        error(custom_messages.ResourceNotFound('SuperPodSize'))
    end
    log:operation(ctx:get_initiator(), 'Chassis', 'Set SuperPodSize to (%d) successfully', SuperPodSize)
    return true
end

function c_super_pod_label_collection:SetSuperPodEnabled(ctx, SuperPodEnabled)
    local super_pod_label_object = c_super_pod_label_object.collection:find({Id = 1})
    if not super_pod_label_object then
        log:operation(ctx:get_initiator(), 'Chassis', 'Set SuperPodEnabled to (%s) failed', SuperPodEnabled)
        error(custom_messages.ResourceNotFound('SuperPodLabel'))
    end

    local res = pcall(super_pod_label_object.update_super_pod_enabled, super_pod_label_object, SuperPodEnabled)
    if not res then
        log:operation(ctx:get_initiator(), 'Chassis', 'Set SuperPodEnabled to (%s) failed', SuperPodEnabled)
        error(custom_messages.ResourceNotFound('SuperPodEnabled'))
    end
    log:operation(ctx:get_initiator(), 'Chassis', 'Set SuperPodEnabled to (%s) successfully', SuperPodEnabled)
    return
end

function c_super_pod_label_collection:RestoreDefaults(ctx)
    local super_pod_label_object = c_super_pod_label_object.collection:find({Id = 1})
    if not super_pod_label_object then
        log:operation(ctx:get_initiator(), 'Chassis', 'Restore SuperPodLabel to the default value failed')
        error(custom_messages.ResourceNotFound('SuperPodLabel'))
    end
    local res = super_pod_label_object:restore_defaults()
    if not res then
        log:operation(ctx:get_initiator(), 'Chassis', 'Restore SuperPodLabel to the default value failed')
        error(custom_messages.ResourceNotFound('SuperPodLabel'))
    end
    log:operation(ctx:get_initiator(), 'Chassis', 'Restore SuperPodLabel to the default value successfully')
    return true
end

function c_super_pod_label_collection:ipmi_get_superpod_label_info(req, ctx)
    local rsp = ipmi_msg.GetSuperPodLabelInfoRsp.new()
    rsp.ManufactureId = 0x0007db
    rsp.EndFlag = 0
    local super_pod_label_object = c_super_pod_label_object.collection:find({Id = 1})
    if not super_pod_label_object then
        ipmi.ipmi_operation_log(ctx, 'Chassis', 'Get SuperPodLabel failed')
        rsp.CompletionCode = cc.DataNotAvailable
        return rsp
    end
    rsp.Data = bs.new('<<SuperPodId:4/unit:8,ServerIndex:2/unit:8,SuperPodSize:2/unit:8>>'):pack({
        SuperPodId = super_pod_label_object.SuperPodId,
        ServerIndex = super_pod_label_object.ServerIndex,
        SuperPodSize = super_pod_label_object.SuperPodSize
    })
    rsp.CompletionCode = cc.Success
    return rsp
end

function c_super_pod_label_collection:ipmi_set_superpod_label_info(req, ctx)
    local rsp = ipmi_msg.SetSuperPodLabelInfoRsp.new()
    rsp.ManufactureId = 0x0007db
    local super_pod_label_object = c_super_pod_label_object.collection:find({Id = 1})
    if not super_pod_label_object then
        ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set SuperPodLabel failed')
        rsp.CompletionCode = cc.DataNotAvailable
        return rsp
    end
    local super_pod_id = (req.Data:byte(4) << 24) | req.Data:byte(3) << 16 | (req.Data:byte(2) << 8) | req.Data:byte(1)
    local server_index = (req.Data:byte(6) << 8) | req.Data:byte(5)
    local super_pod_size =  (req.Data:byte(8) << 8) | req.Data:byte(7)
    if super_pod_size < MIN_SUPERPOD_SIZE or super_pod_size > MAX_SUPERPOD_SIZE then
        ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set SuperPodLabel failed')
        rsp.CompletionCode = cc.ParmOutOfRange
        return rsp
    end
    local res = super_pod_label_object:update_super_pod_id(super_pod_id)
    res = super_pod_label_object:update_server_index(server_index) and res
    res = super_pod_label_object:update_super_pod_size(super_pod_size) and res
    if not res then
        ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set SuperPodLabel failed')
        rsp.CompletionCode = cc.ResponseError
        return rsp
    end
    ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set SuperPodId to (%s), \
                    ServerIndex to (%s), SuperPodSize to (%s) successfully',
        super_pod_id, server_index, super_pod_size)
    rsp.CompletionCode = cc.Success
    return rsp
end

function c_super_pod_label_collection:ipmi_set_super_pod_id(req, ctx)
    local rsp = ipmi_msg.SetSuperPodIdRsp.new()
    rsp.ManufactureId = 0x0007db
    local super_pod_label_object = c_super_pod_label_object.collection:find({Id = 1})
    if not super_pod_label_object then
        ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set SuperPodId failed')
        rsp.CompletionCode = cc.DataNotAvailable
        return rsp
    end
    local super_pod_id = (req.Data:byte(4) << 24) | req.Data:byte(3) << 16 | (req.Data:byte(2) << 8) | req.Data:byte(1)
    local res = super_pod_label_object:update_super_pod_id(super_pod_id)
    if not res then
        ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set SuperPodId failed')
        rsp.CompletionCode = cc.ResponseError
        return rsp
    end
    ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set SuperPodId to (%s) successfully', super_pod_id)
    rsp.CompletionCode = cc.Success
    return rsp
end

function c_super_pod_label_collection:ipmi_set_server_index(req, ctx)
    local rsp = ipmi_msg.SetServerIndexRsp.new()
    rsp.ManufactureId = 0x0007db
    local super_pod_label_object = c_super_pod_label_object.collection:find({Id = 1})
    if not super_pod_label_object then
        ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set ServerIndex failed')
        rsp.CompletionCode = cc.DataNotAvailable
        return rsp
    end
    local server_index = (req.Data:byte(2) << 8) | req.Data:byte(1)
    local res = super_pod_label_object:update_server_index(server_index)
    if not res then
        ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set ServerIndex failed')
        rsp.CompletionCode = cc.ResponseError
        return rsp
    end
    ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set ServerIndex to (%s) successfully', server_index)
    rsp.CompletionCode = cc.Success
    return rsp
end

function c_super_pod_label_collection:ipmi_set_super_pod_size(req, ctx)
    local rsp = ipmi_msg.SetSuperPodSizeRsp.new()
    rsp.ManufactureId = 0x0007db
    local super_pod_label_object = c_super_pod_label_object.collection:find({Id = 1})
    if not super_pod_label_object then
        ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set SuperPodSize failed')
        rsp.CompletionCode = cc.DataNotAvailable
        return rsp
    end
    local super_pod_size = (req.Data:byte(2) << 8) | req.Data:byte(1)
    local res = super_pod_label_object:update_super_pod_size(super_pod_size)
    if not res then
        ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set SuperPodSize failed')
        rsp.CompletionCode = cc.ResponseError
        return rsp
    end
    ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set SuperPodSize to (%s) successfully', super_pod_size)
    rsp.CompletionCode = cc.Success
    return rsp
end

function c_super_pod_label_collection:ipmi_set_super_pod_enabled(req, ctx)
    local rsp = ipmi_msg.SetSuperPodEnabledRsp.new()
    rsp.ManufactureId = 0x0007db
    local obj_list = client:GetFruCtrlObjects()
    if not next(obj_list) then
        ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set SuperPodEnabled failed')
        log:error("get fru objects failed")
        rsp.CompletionCode = cc.DataNotAvailable
        return rsp
    end
    for _, obj in pairs(obj_list) do 
        if obj.PowerState ~= "OFF" then
            ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set SuperPodEnabled failed')
            log:error("power state : %s, expected off", obj.PowerState)
            rsp.CompletionCode = cc.DataNotAvailable
            return rsp
        end
    end
    local super_pod_label_object = c_super_pod_label_object.collection:find({Id = 1})
    if not super_pod_label_object then
        ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set SuperPodEnabled failed')
        log:error("get super_pod_label object failed")
        rsp.CompletionCode = cc.DataNotAvailable
        return rsp
    end
    local super_pod_enabled = req.Data:byte(1)
    if super_pod_enabled == SUPERPOD_LABEL_DISABLED then
        super_pod_enabled = false
    elseif super_pod_enabled == SUPERPOD_LABEL_ENABLED then
        super_pod_enabled = true
    else
        ipmi.ipmi_operation_log(ctx, 'Chassis', 'Set SuperPodEnabled failed')
        rsp.CompletionCode = cc.ParmOutOfRange
        return rsp
    end
    super_pod_label_object:update_super_pod_enabled(super_pod_enabled)
    rsp.CompletionCode = cc.Success
    return rsp
end

function c_super_pod_label_collection:ipmi_get_super_pod_enabled(req, ctx)
    local rsp = ipmi_msg.GetSuperPodLabelInfoRsp.new()
    rsp.ManufactureId = 0x0007db
    rsp.EndFlag = 0
    local super_pod_label_object = c_super_pod_label_object.collection:find({Id = 1})
    if not super_pod_label_object then
        ipmi.ipmi_operation_log(ctx, 'Chassis', 'Get SuperPodEnabled failed')
        rsp.CompletionCode = cc.DataNotAvailable
        return rsp
    end
    local super_pod_enabled = super_pod_label_object.SuperPodEnabled
                                    and SUPERPOD_LABEL_ENABLED
                                    or SUPERPOD_LABEL_DISABLED
    rsp.Data = bs.new('<<SuperPodEnabled:1/unit:8>>'):pack({
        SuperPodEnabled = super_pod_enabled
    })
    rsp.CompletionCode = cc.Success
    return rsp
end

return singleton(c_super_pod_label_collection)
