-- 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_volume = require 'volume.volume_object'
local singleton = require 'mc.singleton'
local class = require 'mc.class'
local signal = require 'mc.signal'
local c_tasks = require 'mc.orm.tasks'

local VOLUME_NAME = 'Volume'

local volume_collection = class(nil, nil, true)

function volume_collection:ctor(storage_app_service)
    self.storage_app_service = storage_app_service
    self.bus = storage_app_service.bus
    self.object_manager = storage_app_service.mc_obj_manager
    self.on_add_volume = signal.new()
    self.on_del_volume = signal.new()
    self.on_update_drive_list = signal.new()
    self.on_update_array_by_volume = signal.new()
    self.on_update_spare_drive_list = signal.new()
    self.on_clear_drives_failed_array_alarm = signal.new()
    self.volume_list = {}
end

function volume_collection:init()
    self.on_add_volume:on(function(volume_obj)
        -- 创建volume对象
        local obj = self.storage_app_service:CreateVolume(1, volume_obj.path, volume_obj.volume_id)
        self.volume_list[string.format('%d:%d', volume_obj.controller_id, volume_obj.volume_id)] = obj
        obj.Id = volume_obj.volume_id
        obj.RefControllerId = volume_obj.controller_id
        -- 订阅对象
        self.object_manager.mc:add_object(VOLUME_NAME, obj, volume_obj.controller_id)
        self:init_update_drive_list(volume_obj.controller_id, volume_obj.volume_id)
        self:init_update_array_by_volume(volume_obj.controller_id, volume_obj.volume_id)
        self:init_update_spare_drive_list(volume_obj.controller_id, volume_obj.volume_id)
    end)

    self.on_del_volume:on(function(volume_obj)
        -- 获取要删除的对象
        local obj = self:get_ctrl_volume_by_id(volume_obj.controller_id, volume_obj.volume_id)
        if obj then
            -- del_object已包含从资源树上删除对象
            self.object_manager.mc:del_object(VOLUME_NAME, obj, volume_obj.controller_id)
            c_volume:check_abnormal_volume_exists()
            self.on_clear_drives_failed_array_alarm:emit(obj)
        end
    end)
    self:start_update_volume_info_task()
end

-- os启停的时候，会删除掉逻辑盘，所以collection中应该没有volume对象了
function volume_collection:start_update_volume_info_task()
    c_tasks.get_instance():new_task('update volume info task'):loop(function(task)
        c_volume.collection:fold(function(acc, obj, key)
            obj:get_volume_info()
        end, {})
    end):set_timeout_ms(30000)
end

function volume_collection:get_ctrl_volume_by_id(controller_id, volume_id)
    local volume_obj = c_volume.collection:find(function(obj)
        if obj.RefController == controller_id and obj.Id == volume_id then
            return obj
        end
    end)
    return volume_obj
end

function volume_collection:init_update_drive_list(controller_id, volume_id)
    local volume_obj = self:get_ctrl_volume_by_id(controller_id, volume_id)
    volume_obj.on_update_ref_drives:on(function(obj)
        self.on_update_drive_list:emit(obj)
    end)
end

function volume_collection:init_update_array_by_volume(controller_id, volume_id)
    local volume_obj = self:get_ctrl_volume_by_id(controller_id, volume_id)
    volume_obj.on_update_array_by_volume:on(function(obj)
        self.on_update_array_by_volume:emit(obj)
    end)
end

function volume_collection:init_update_spare_drive_list(controller_id, volume_id)
    local volume_obj = self:get_ctrl_volume_by_id(controller_id, volume_id)
    volume_obj.on_update_spare_drive_list:on(function(obj, pd_ids)
        self.on_update_spare_drive_list:emit(obj, pd_ids)
    end)
end

function volume_collection:dump_info(fp_w, controller_id)
    if not next(self.volume_list) then
        fp_w:write("Not found logical drive.\n\n\n")
        return
    end

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

return singleton(volume_collection)