-- 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 c_object = require 'object_manager.object'
local sml = require 'sml'
local signal = require 'mc.signal'
local ctrl_commu_loss_monitor = require 'ctrl_commu_loss_monitor'
local array_dump = require 'array.array_dump'
local common_def = require 'common_def'
local log = require 'mc.logging'

local function make_array_key(controller_id, array_id)
    return string.format('%d:%d', controller_id, array_id)
end

---@class c_array: c_object
---@field Id integer
---@field UsedSpaceMiB integer
---@field TotalFreeSpaceMiB integer
---@field FreeBlocksSpaceMiB integer[]
---@field RefVolumes integer[]
---@field RefDrives string[]
---@field RefPDSlots integer[]
---@field RefPDEnclosures integer[]
---@field RefControllerId integer
---@field new fun(...): c_array
local c_array = c_object('DiskArray', { 'key' }) -- DiskArray 类，主键是 key 字段

local TASK_UPDATE <const> = 'update'

function c_array:ctor(obj)
    self.RefController = self.position
    self.key = make_array_key(self.RefController, obj.Id)
    self.RefPDSlots = {}
    self.RefPDEnclosures = {}
    self.on_update = signal.new()
    self.on_update_ref_drives = signal.new()
end

function c_array:dtor()
    self:stop()
end

function c_array:init()
    self.RefControllerId = self.RefController
    self:set_default_values()
    self.on_update:on(function(info)
        self:update_array_info(info)
    end)

    self:start()
end

function c_array:start()
    local ok, ret, controller_id
    log:notice('Start update array info.')
    self:new_task({ TASK_UPDATE, self.key }):loop(function(task)
        if task.is_exit then
            return
        end
        controller_id = self.RefControllerId
        ok, ret = pcall(sml.get_ctrl_init_state, controller_id)
        if not ok or ret ~= 2 then
            return
        end
        ok, ret = pcall(sml.get_array_info, self.RefControllerId, self.Id)
        if not ok then
            if ret ~= common_def.SML_ERR_CTRL_STATUS_INVALID and ret ~= common_def.SML_ERR_NULL_INFTERFACE then
                ctrl_commu_loss_monitor.get_instance():update(true, self.RefControllerId)
            end
            return
        end
        if task.is_exit then
            return
        end
        ctrl_commu_loss_monitor.get_instance():update(false, self.RefControllerId)
        self.on_update:emit(ret)
    end):set_timeout_ms(30000)
end

function c_array:stop()
    self:stop_tasks()
    log:notice('Stop update array info.')
end

---@param info c_array_info
function c_array:update_array_info(info)
    self.UsedSpaceMiB = info.used_space
    self.TotalFreeSpaceMiB = info.total_free_space
    self.FreeBlocksSpaceMiB = info.free_blocks_space
    self.RefVolumes = info.ld_ids
    self.RefPDSlots = info.pd_slots
    self.RefPDEnclosures = info.pd_enclosures
    self.on_update_ref_drives:emit(self)
end

function c_array:set_default_values()
    self.UsedSpaceMiB = common_def.INVALID_U32
    self.TotalFreeSpaceMiB = common_def.INVALID_U32
    self.FreeBlocksSpaceMiB = {}
    self.RefVolumes = {}
    self.RefPDSlots = {}
    self.RefPDEnclosures = {}
    self.RefDrives = {}
end

function c_array:dump_info(fp_w)
    array_dump.get_instance():dump_info(fp_w, self)
end

return c_array
