-- Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
--
-- this file licensed under the 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 skynet = require 'skynet'
local log = require 'mc.logging'
local utils = require 'mc.utils'
local signal = require 'mc.signal'

local SEC_TO_SKYNET_SCALE<const> = 100

local scheduler = class()

function scheduler:set_period(period_in_sec)
    if period_in_sec <= 0 then
        return
    end
    self.period_in_sec = period_in_sec
end

function scheduler:update_params(params)
    self.params = params
end

function scheduler:emit_signal_on_data_change(ret)
    if self.is_running then
        self.on_data_change:emit(ret)
    end
end

function scheduler:emit_signal_on_error()
    if self.is_running then
        self.on_error:emit()
    end
end

local function exec(s)
    while true do
        if not s.is_running then
            return
        end
        if s.is_paused then
            skynet.sleep(s.period_in_sec * SEC_TO_SKYNET_SCALE)
            goto continue
        end   
        local rsp = s.func(s.obj, s.params)
        if rsp then
            local ret = rsp
            if #rsp > 0 and s.parser then
                ret = s.parser(rsp)
            end
            if not utils.table_compare(s.data, ret) then
                skynet.fork(s.emit_signal_on_data_change, s, ret)
            end
            s.data = ret
        else
            s.data = nil
            skynet.fork(s.emit_signal_on_error, s)
        end
        if s.main_task then
            skynet.wakeup(s.main_task)
            s.main_task = nil
        end
        skynet.sleep(s.period_in_sec * SEC_TO_SKYNET_SCALE)
        ::continue::
    end

end

function scheduler:start()
    if not self.is_running then
        if self.period_in_sec <= 0 then
            log:error('period is not set, unable to start scheduler to scan')
            return nil
        end
        skynet.fork(exec, self)
        self.is_running = true
    end
    self.main_task = coroutine.running()
    skynet.wait(self.main_task)

    return self.data
end

function scheduler:pause()
    self.is_paused = true
    log:info('pause %s scheduler to scan', self.name)
end

function scheduler:resume()
    self.is_paused = false
    log:info('resume %s scheduler to scan', self.name)
end

function scheduler:deconstruct()
    self.is_running = false
end

function scheduler:ctor(func, obj, params, parser)
    self.obj = obj
    self.func = func
    self.params = params
    self.period_in_sec = params.period_in_sec
    self.name = params.name
    self.parser = parser
    self.data = nil
    self.is_running = false
    self.is_paused = false
    self.on_data_change = signal.new()
    self.on_delete = signal.new()
    self.on_error = signal.new()

end

return scheduler
