-- 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_array = require 'array.array_object'
local class = require 'mc.class'
local signal = require 'mc.signal'
local c_tasks = require 'mc.orm.tasks'
local log = require 'mc.logging'

local DISK_ARRAY_NAME <const> = 'DiskArray'

local c_array_collection = class(nil, nil, true)

function c_array_collection:ctor(storage_app_service)
    self.array_list = {}
    self.storage_app_service = storage_app_service
    self.object_manager = storage_app_service.mc_obj_manager
    self.on_add_array = signal.new()
    self.on_del_array = signal.new()
    self.on_update_drive_list = signal.new()
end

-- 测试os下电时候，这里会不会发送插件请求
function c_array_collection:start_update_array_info_task()
    c_tasks.get_instance():new_task('update array info task'):loop(function(task)
        c_array.collection:fold(function(acc, obj, key)
                pcall(obj.get_array_info, obj)
        end, {})
    end):set_timeout_ms(30000)
end

function c_array_collection:init()
    self.on_add_array:on(function(controller_id, controller_path, array_id)
        -- 在资源树上创建对象
        local obj = self.storage_app_service:CreateDiskArray(1, controller_path, array_id)
        self.array_list[string.format('%d:%d', controller_id, array_id)] = obj
        obj.Id = array_id
        obj.RefControllerId = controller_id
        -- 创建受模块管理的对象
        self.object_manager.mc:add_object(DISK_ARRAY_NAME, obj, controller_id)
        self:init_update_drive_list(controller_id, array_id)
    end)
    self.on_del_array:on(function(controller_id, array_id)
        local obj = self:get_ctrl_array_by_id(controller_id, array_id)
        if obj then
            -- 删除受模块管理的对象，del_object已包含了从资源树删除对象的逻辑
            self.object_manager.mc:del_object(DISK_ARRAY_NAME, obj, controller_id)
        end
    end)
    self:start_update_array_info_task()
end

function c_array_collection:init_update_drive_list(controller_id, array_id)
    local array_obj = self:get_ctrl_array_by_id(controller_id, array_id)
    array_obj.on_update_ref_drives:on(function(obj)
        self.on_update_drive_list:emit(obj)
    end)
end

-- 以控制器Id为下标，注意不能多线程访问
local array_list = {}

function c_array_collection:get_ctrl_array_list_by_ctrl_id(controller_id)
    if array_list[controller_id] == nil then
        array_list[controller_id] = {}
    else
        for k, _ in pairs(array_list[controller_id]) do
            array_list[controller_id][k] = nil
        end
    end

    c_array.collection:fold(function(acc, obj, key)
        if obj.RefControllerId == controller_id then
            array_list[controller_id][obj.Id] = true
        end
    end, {})
    return array_list[controller_id]
end

function c_array_collection:get_ctrl_array_by_id(controller_id, array_id)
    return c_array.collection:find({ RefControllerId = controller_id, Id = array_id })
end

-- 根据diskname获取对应的array对象
function c_array_collection:get_ctrl_array_by_diskname(diskname)
    local array_id = nil
    local controller_id = nil
    c_array.collection:fold(function(acc, obj, key)
        for _, name in pairs(obj.RefDrives) do
            if name == diskname then
                array_id = obj.Id
                controller_id = obj.RefControllerId
                return nil
            end
        end
    end, {})
    if array_id and controller_id then
        return self:get_ctrl_array_by_id(controller_id, array_id)
    end
    return nil
end

function c_array_collection:dump_info(fp_w, controller_id)
    if not next(self.array_list) then
        fp_w:write("Not found disk array.\n\n\n")
        return
    end

    local count = 0
    c_array.collection:fold(function(acc, obj, key)
        if obj.RefControllerId == controller_id then
            count = count + 1
            obj:dump_info(fp_w)
        end
    end, {})
    if count == 0 then
        fp_w:write("No disk array in this controller.\n")
    end
    fp_w:write("\n\n")
end

return singleton(c_array_collection)
