-- 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 class = require 'mc.class'
local c_tasks = require 'tasks'
local signal = require 'mc.signal'
local c_factory = require 'object_manager.factory'
local c_object_collection = require 'object_manager.object_collection'
require 'object_manager.basic'

-- c_object：自发现对象包装类
-- 一些基础机制可以在这里实现，比如任务
---@class c_object: c_basic_class
---@field dtor function
---@field base_obj any
---@field collection c_object_collection
---@field on_add_object c_basic_signal
---@field on_add_object_complete c_basic_signal
---@field on_delete_object c_basic_signal
---@field on_delete_object_complete c_basic_signal
---@field object_id integer
---@field on_property_changed c_basic_signal
---@field __class_name nil | string
local c_object = class(nil, nil, true)

function c_object:ctor(obj, position)
    self.base_obj = obj
    self.position = position
    self.tasks = false
    self.on_property_changed = signal.new()
    self.task_count = 0
end

function c_object:dtor()
    c_object.stop_tasks(self)
end

function c_object:get_key(keys)
    if type(keys) == 'function' then
        return keys(self)
    elseif type(keys) == 'table' then
        if #keys == 0 then
            return nil
        elseif #keys == 1 then
            return self[keys[1]]
        end

        local values = {}
        for _, key in ipairs(keys) do
            local val = self[key]
            if val == nil then
                return nil
            end
            values[#values + 1] = self[key]
        end
        return table.concat(values, ':')
    end
end

function c_object:class_name()
    return self.__class_name or 'object'
end

---@param name string | string[]
---@return string
function c_object:task_name(name)
    local names
    if type(name) == 'table' then
        names = { self.__class_name, table.unpack(name) }
    else
        names = { self.__class_name, name }
    end
    return table.concat(names, '.')
end

-- 创建任务
---@param name string | (string|integer)[]
function c_object:new_task(name)
    -- 这里延迟创建 tasks 表，因为不是每个对象都需要任务
    if not self.tasks then
        self.tasks = {}
        self.task_count = 0
    end

    local task_name = self:task_name(name)
    if self.tasks[task_name] then
        return self.tasks[task_name]
    end

    local task = c_tasks.get_instance():new_task(task_name)
    self.tasks[task.name] = task
    task.on_task_exit:on(function()
        if not self.tasks[task.name] then
            return
        end

        self.tasks[task.name] = nil
        self.task_count = self.task_count - 1
    end)
    self.task_count = self.task_count + 1
    return task
end

---@param name string | string[]
function c_object:get_task(name)
    if not self.tasks then
        return nil
    end

    return self.tasks[self:task_name(name)]
end

function c_object:get_task_count()
    return self.task_count or 0
end

function c_object:stop_tasks()
    local ts = self.tasks
    if not ts then
        return
    end

    self.tasks = {}

    for _, task in pairs(ts) do
        task:stop()
    end
end

---@param name string | string[]
function c_object:stop_task(name)
    if not self.tasks then
        return
    end

    local task_name = self:task_name(name)
    local t = self.tasks[task_name]
    if t then
        t:stop()
    end
end

local function reset_signal(sig)
    if not sig then
        return signal.new()
    end

    sig.handles = {}
    return sig
end

local function init_class_signal(cls)
    cls.on_add_object = reset_signal(cls.on_add_object)
    cls.on_add_object_complete = reset_signal(cls.on_add_object_complete)
    cls.on_delete_object = reset_signal(cls.on_delete_object)
    cls.on_delete_object_complete = reset_signal(cls.on_delete_object_complete)
end

---@param class_name string
return function(class_name, primary_keys)
    local cls = class(c_object)

    function cls:ctor()
        self.object_id = cls.__object_id
    end

    function cls:__inc_object_id()
        cls.__object_id = cls.__object_id + 1
    end
    function cls:__reset()
        cls.__object_id = 1
        cls.collection:reset()
        init_class_signal(cls)
    end

    cls.__object_id = 1
    cls.__class_name = class_name
    init_class_signal(cls)
    cls.collection = c_object_collection.new(cls, primary_keys)
    cls.on_delete_object:on(function(object)
        object:stop_tasks()
    end)

    local c_basic_object = class(cls, {
        -- 重载 __index 和 __newindex 操作符，方便对自发现对象进行操作
        __index = function(self, name)
            return self.base_obj[name]
        end,
        __newindex = function(self, name, value)
            self.base_obj[name] = value
        end
    })
    c_factory.get_instance():register(class_name, c_basic_object)
    return c_basic_object
end