-- 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 class_mgmt = require 'mc.class_mgnt'

local VOLUME_NAME = 'Volume'

local volume_collection = class()

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.object_manager
    self.on_add_volume = signal.new()
    self.on_del_volume = signal.new()
    self.on_controller_commu_changed = 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
        -- 订阅对象
        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
            local key = string.format('%d:%d', volume_obj.controller_id, volume_obj.volume_id)
            class_mgmt(VOLUME_NAME):remove(self.volume_list[key])
            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.on_controller_commu_changed:on(function(mctp_state, controller_id)
        if mctp_state then
            c_volume.collection:fold(function(acc, obj, key)
                if obj.RefController == controller_id then
                    obj:start_update_tasks()
                end
            end, {})
        else
            c_volume.collection:fold(function(acc, obj, key)
                if obj.RefController == controller_id then
                    obj:stop_tasks()
                end
            end, {})
        end
    end)
end

function volume_collection:get_ctrl_volume_by_id(controller_id, volume_id)
    return c_volume.collection:find({
        RefController = controller_id,
        Id = volume_id
    })
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)