-- 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 log = require 'mc.logging'
local singleton = require 'mc.singleton'
local class = require 'mc.class'
local skynet = require 'skynet'
local bios_enum = require 'domain.bios_firmware.defs'

local UpgradeStateMachaine = class()

local UpgradeState = {
    UPGRADE_READY = "UPGRADE_READY",
    PREPARE_START = "PREPARE_START",
    PREPARE_FINISH = "PREPARE_FINISH",
    PROCESS_START = "PROCESS_START",
    PROCESS_FINISH = "PROCESS_FINISH",
    FIRMWARE_FINISHING = "FIRMWARE_FINISHING"
}

function UpgradeStateMachaine:ctor()
    self.state = {}
    self.state['bios'] = UpgradeState.UPGRADE_READY
    self.state['krun'] = UpgradeState.UPGRADE_READY
    self.lock_machine = {}
    self.lock_machine['bios'] = false
    self.lock_machine['krun'] = false
    self.states_fun = {
        [UpgradeState.UPGRADE_READY] = UpgradeStateMachaine.upgrade_ready,
        [UpgradeState.PREPARE_START] = UpgradeStateMachaine.prepare_start,
        [UpgradeState.PREPARE_FINISH] = UpgradeStateMachaine.prepare_finish,
        [UpgradeState.PROCESS_START] = UpgradeStateMachaine.process_start,
        [UpgradeState.PROCESS_FINISH] = UpgradeStateMachaine.process_finish,
        [UpgradeState.FIRMWARE_FINISHING] = UpgradeStateMachaine.firmware_finishing
    }
end

function UpgradeStateMachaine:lock(timeout, firmware_type)
    if self.state[firmware_type] == UpgradeState.UPGRADE_READY and self.lock_machine[firmware_type] == false then
        self.lock_machine[firmware_type] = true
        return
    end
    while timeout > 0 do
        if self.state[firmware_type] == UpgradeState.UPGRADE_READY then
            self.lock_machine[firmware_type] = true
            return
        end
        skynet.sleep(100)
        timeout = timeout - 1
    end
    error('lock %s upgrade machine fail', firmware_type)
end

function UpgradeStateMachaine:unlock(firmware_type)
    self.lock_machine[firmware_type] = false
end

function UpgradeStateMachaine:wait_until_unlock(timeout, firmware_type)
    while timeout > 0 do
        if not self.lock_machine[firmware_type] then
            return
        end
        skynet.sleep(100)
        timeout = timeout - 1
    end
    error('wait unlock upgrade machine timeout')
end

function UpgradeStateMachaine:run(event, firmware_type)
    if self.lock_machine and self.state[firmware_type] == UpgradeState.PREPARE_FINISH then
        log:notice('state machine is locked, need wait')
        self:wait_until_unlock(600, firmware_type)
    end
    local get_state_map = self.states_fun[self.state[firmware_type]]
    local state_map = get_state_map(self)
    local pre_state = self.state[firmware_type]
    local state = state_map[event]
    if state then
        self.state[firmware_type] = state
        log:notice('[%s]state change from %s to %s', firmware_type, pre_state, state)
    else
        log:error('[%s]current state(%s) event(%s) not support', firmware_type, self.state, event)
        error('event not support')
    end
end

function UpgradeStateMachaine:upgrade_ready()
    return {
        [bios_enum.UpgradeEvent.UpgradeReady] = UpgradeState.UPGRADE_READY,
        [bios_enum.UpgradeEvent.PrepareStart] = UpgradeState.PREPARE_START,
        [bios_enum.UpgradeEvent.Error] = UpgradeState.UPGRADE_READY
    }
end

function UpgradeStateMachaine:prepare_start()
    return {
        [bios_enum.UpgradeEvent.PrepareFinish] = UpgradeState.PREPARE_FINISH,
        [bios_enum.UpgradeEvent.Error] = UpgradeState.UPGRADE_READY
    }
end

function UpgradeStateMachaine:prepare_finish()
    return {
        [bios_enum.UpgradeEvent.PrepareStart] = UpgradeState.PREPARE_START,
        [bios_enum.UpgradeEvent.ProcessStart] = UpgradeState.PROCESS_START,
        [bios_enum.UpgradeEvent.Error] = UpgradeState.UPGRADE_READY
    }
end

function UpgradeStateMachaine:process_start()
    return {
        [bios_enum.UpgradeEvent.ProcessFinish] = UpgradeState.PROCESS_FINISH,
        [bios_enum.UpgradeEvent.Error] = UpgradeState.UPGRADE_READY
    }
end

function UpgradeStateMachaine:process_finish()
    return {
        [bios_enum.UpgradeEvent.FimwareStart] = UpgradeState.FIRMWARE_FINISHING,
        [bios_enum.UpgradeEvent.Error] = UpgradeState.UPGRADE_READY
    }
end

function UpgradeStateMachaine:firmware_finishing()
    return {
        [bios_enum.UpgradeEvent.FimwareFinish] = UpgradeState.UPGRADE_READY,
        [bios_enum.UpgradeEvent.Error] = UpgradeState.UPGRADE_READY
    }
end

function UpgradeStateMachaine:get_state(firmware_type)
    return self.state[firmware_type]
end

return singleton(UpgradeStateMachaine)