-- 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 skynet = require 'skynet'
local class = require 'mc.class'
local log = require 'mc.logging'
local m_enums = require 'types.enums'
local utils = require 'types.utils'
local coroutine = require 'skynet.coroutine'
local utils_core = require 'utils.core'
local UART_NAME<const> = '/dev/ttyS0'
local multihost = require 'multi_host'

-- class power_state
local hs_state = class()

local HotSwapState = {
    M0 = "M0",
    M1 = "M1",
    M2 = "M2",
    M3 = "M3",
    M4 = "M4",
    M5 = "M5",
    M6 = "M6",
    M7 = "M7"
}

function hs_state:ctor(fructrl, id)
    self.fructrl = fructrl
    self.state = HotSwapState.M0
    self.system_id = id
    self.serial_print = utils.print_host_info(multihost.get_instance():is_multihost_type() and self.system_id or nil)

    -- 创建协程
    self.co = coroutine.create(function()
        local evt
        while true do
            -- 阻塞，获取resume发来的事件
            evt = coroutine.yield()
            self:state_machine_run(evt)
        end
    end)
    coroutine.resume(self.co)
end

function hs_state:init()
    self:send_hotswap_event(tostring(m_enums.HotswapEventTypes.FruPinMated))
end

function hs_state:send_hotswap_event(val)
    if val == "" then
        return
    end
    -- 发送事件
    coroutine.resume(self.co, val)
end

function hs_state:get_hs_state()
    return m_enums.HotswapStateToNum[self.state]
end

function hs_state:hs_state_m0(event)
    log:notice("Current event is %s, state is M0, uptime: %s.", event, utils.uptime())

    if event == tostring(m_enums.HotswapEventTypes.FruPinMated) then
        -- 状态跳转
        self.state = HotSwapState.M1
        log:notice("[System:%s]Move M0 to M1, uptime: %s.", self.system_id, utils.uptime())
        log:notice_printf("%sMove M0 to M1", self.serial_print)
        log:notice_printf('%sManaged FRU0 state changed, cause is %s.......................M0->M1',
            self.serial_print, event)

        skynet.fork_once(function()
            self:hs_action_m0_m1()
        end)
        return
    end
end

function hs_state:hs_state_m1(event)
    log:notice("[System:%s]Current event is %s, state is M1, uptime: %s.", self.system_id, event, utils.uptime())

    if event == tostring(m_enums.HotswapEventTypes.FruInsertionCriteriaMet) then
        self.state = HotSwapState.M2
        log:notice("[System:%s]Move M1 to M2, uptime: %s.", self.system_id, utils.uptime())
        log:notice_printf("%sMove M1 to M2", self.serial_print)
        log:notice_printf('%sManaged FRU0 state changed, cause is %s.......................M1->M2',
            self.serial_print, event)

        skynet.fork_once(function()
            self:hs_action_m1_m2()
        end)
        return
    end
end

function hs_state:hs_state_m2(event)
    log:notice("[System:%s]Current event is %s, state is M2, uptime: %s.", self.system_id, event, utils.uptime())

    if event == tostring(m_enums.HotswapEventTypes.FruActivated) then
        self.state = HotSwapState.M3
        log:notice("[System:%s]Move M2 to M3, uptime: %s.", self.system_id, utils.uptime())
        log:notice_printf("%sMove M2 to M3", self.serial_print)
        log:notice_printf('%sManaged FRU0 state changed, cause is %s.......................M2->M3',
            self.serial_print, event)

        skynet.fork_once(function()
            self:hs_action_m2_m3()
        end)
        return
    end
    if event == tostring(m_enums.HotswapEventTypes.FruExtractionCriteriaMet) or
       event == tostring(m_enums.HotswapEventTypes.FruDeactivated) then
        self.state = HotSwapState.M1
        log:notice("[System:%s]Move M2 to M1, uptime: %s.", self.system_id, utils.uptime())
        log:notice_printf("%sMove M2 to M1", self.serial_print)
        log:notice_printf('%sManaged FRU0 state changed, cause is %s.......................M2->M1',
            self.serial_print, event)

        skynet.fork_once(function()
            self:hs_action_m2_m1()
        end)
        return
    end
end

function hs_state:hs_state_m3(event)
    log:notice("[System:%s]Current event is %s, state is M3, uptime: %s.", self.system_id, event, utils.uptime())

    if event == tostring(m_enums.HotswapEventTypes.FruActivatedCompleted) then
        self.state = HotSwapState.M4
        log:notice("[System:%s]Move M3 to M4, uptime: %s.", self.system_id, utils.uptime())
        log:notice_printf("%sMove M3 to M4", self.serial_print)
        log:notice_printf('%sManaged FRU0 state changed, cause is %s.......................M3->M4',
            self.serial_print, event)

        skynet.fork_once(function()
            self:hs_action_m3_m4()
        end)
        return
    end
    if event == tostring(m_enums.HotswapEventTypes.FruDeactivated) or
       event == tostring(m_enums.HotswapEventTypes.FruExtractionCriteriaMet) or
       event == tostring(m_enums.HotswapEventTypes.FruPowerFailure) then
        self.state = HotSwapState.M6
        log:notice("[System:%s]Move M3 to M6, uptime: %s.", self.system_id, utils.uptime())
        log:notice_printf("%sMove M3 to M6", self.serial_print)
        log:notice_printf('%sManaged FRU0 state changed, cause is %s.......................M3->M6',
            self.serial_print, event)

        skynet.fork_once(function()
            self:hs_action_m3_m6(event)
        end)
        return
    end
end

function hs_state:hs_state_m4(event)
    log:notice("[System:%s]Current event is %s, state is M4, uptime: %s.", self.system_id, event, utils.uptime())

    if event == tostring(m_enums.HotswapEventTypes.FruExtractionCriteriaMet) then
        self.state = HotSwapState.M5
        log:notice("[System:%s]Move M4 to M5, uptime: %s.", self.system_id, utils.uptime())
        log:notice_printf("%sMove M4 to M5", self.serial_print)
        log:notice_printf('%sManaged FRU0 state changed, cause is %s.......................M4->M5',
            self.serial_print, event)

        skynet.fork_once(function()
            self:hs_action_m4_m5()
        end)
        return
    end
    if event == tostring(m_enums.HotswapEventTypes.FruDeactivated) or
       event == tostring(m_enums.HotswapEventTypes.FruSetPowerLevel0) or
       event == tostring(m_enums.HotswapEventTypes.FruPowerFailure) or
       event == tostring(m_enums.HotswapEventTypes.FruUnexpectedEdactivation) then
            self.state = HotSwapState.M6
            log:notice("[System:%s]Move M4 to M6, uptime: %s.", self.system_id, utils.uptime())
            log:notice_printf("%sMove M4 to M6", self.serial_print)
            log:notice_printf('%sManaged FRU0 state changed, cause is %s.......................M4->M6',
                self.serial_print, event)

            skynet.fork_once(function()
                self:hs_action_m4_m6(event)
            end)
            return
    end
end

function hs_state:hs_state_m5(event)
    log:notice("[System:%s]Current event is %s, state is M5, uptime: %s.", self.system_id, event, utils.uptime())

    if event == tostring(m_enums.HotswapEventTypes.FruInsertionCriteriaMet) or
       event == tostring(m_enums.HotswapEventTypes.FruActivated) then
        self.state = HotSwapState.M4
        log:notice("[System:%s]Move M5 to M4, uptime: %s.", self.system_id, utils.uptime())
        log:notice_printf("%sMove M5 to M4", self.serial_print)
        log:notice_printf('%sManaged FRU0 state changed, cause is %s.......................M5->M4',
            self.serial_print, event)

        skynet.fork_once(function()
            self:hs_action_m5_m4()
        end)
        return
    end
    if event == tostring(m_enums.HotswapEventTypes.FruDeactivated) or
       event == tostring(m_enums.HotswapEventTypes.FruSetPowerLevel0) or
       event == tostring(m_enums.HotswapEventTypes.FruPowerFailure) then
        self.state = HotSwapState.M6
        log:notice("[System:%s]Move M5 to M6, uptime: %s.", self.system_id, utils.uptime())
        log:notice_printf("%sMove M5 to M6", self.serial_print)
        log:notice_printf('%sManaged FRU0 state changed, cause is %s.......................M5->M6',
            self.serial_print, event)

        skynet.fork_once(function()
            self:hs_action_m5_m6()
        end)
        return
    end
end

function hs_state:hs_state_m6(event)
    log:notice("[System:%s]Current event is %s, state is M6, uptime: %s.", self.system_id, event, utils.uptime())

    if event == tostring(m_enums.HotswapEventTypes.FruDeactivatedCompleted) or
       event == tostring(m_enums.HotswapEventTypes.FruPowerFailure) then
        self.state = HotSwapState.M1
        log:notice("[System:%s]Move M6 to M1, uptime: %s.", self.system_id, utils.uptime())
        log:notice_printf("%sMove M6 to M1", self.serial_print)
        log:notice_printf('%sManaged FRU0 state changed, cause is %s.......................M6->M1',
            self.serial_print, event)

        skynet.fork_once(function()
            self:hs_action_m6_m1()
        end)
        return
    end
    if event == tostring(m_enums.HotswapEventTypes.FruUnnomalStateRecovery) then
        self.state = HotSwapState.M4
        log:notice("[System:%s]Move M6 to M4, uptime: %s.", self.system_id, utils.uptime())
        log:notice_printf("%sMove M6 to M4", self.serial_print)
        log:notice_printf('%sManaged FRU0 state changed, cause is %s.......................M6->M4',
            self.serial_print, event)

        skynet.fork_once(function()
            self:hs_action_m6_m4()
        end)
        return
    end
end

function hs_state:hs_state_m7(event)
    log:notice("[System:%s]Current event is %s, state is M7, uptime: %s.", self.system_id, event, utils.uptime())
    log:notice_printf("%sManaged FRU0 state changed, cause is %s", self.serial_print, event)
end


-- 状态机行为，后期扩展主要改这里
function hs_state:hs_action_m0_m1()
    -- 上报状态
    self.fructrl:set_HotswapState(self.state)
end

function hs_state:hs_action_m1_m2()
    -- 上报状态
    self.fructrl:set_HotswapState(self.state)
    -- 发送激活事件
    self:send_hotswap_event(tostring(m_enums.HotswapEventTypes.FruActivated))
end

function hs_state:hs_action_m2_m1()
    -- 上报状态
    self.fructrl:set_HotswapState(self.state)
end

function hs_state:hs_action_m2_m3()
    -- 上报状态
    self.fructrl:set_HotswapState(self.state)
end

function hs_state:hs_action_m3_m4()
    -- 上报状态
    self.fructrl:set_HotswapState(self.state)
end

function hs_state:hs_action_m3_m6(event)
    -- 上报状态
    self.fructrl:set_HotswapState(self.state)

    if event == tostring(m_enums.HotswapEventTypes.FruPowerFailure) then
        -- 先写空再发送相同事件
        self:send_hotswap_event("")
        skynet.sleep(1)
        self:send_hotswap_event(event)
    end
end

function hs_state:hs_action_m4_m5()
    -- 上报状态
    self.fructrl:set_HotswapState(self.state)
end

function hs_state:hs_action_m4_m6(event)
    -- 上报状态
    self.fructrl:set_HotswapState(self.state)

    if event == tostring(m_enums.HotswapEventTypes.FruPowerFailure) then
        self:send_hotswap_event("")
        skynet.sleep(1)
        self:send_hotswap_event(event)
    end
end

function hs_state:hs_action_m5_m4()
    -- 上报状态
    self.fructrl:set_HotswapState(self.state)
end

function hs_state:hs_action_m5_m6()
    -- 上报状态
    self.fructrl:set_HotswapState(self.state)
end

function hs_state:hs_action_m6_m1()
    -- 上报状态
    self.fructrl:set_HotswapState(self.state)
end

function hs_state:hs_action_m6_m4()
    -- 上报状态
    self.fructrl:set_HotswapState(self.state)
end

-- 本表的定义需要在hs_state_x等接口定义之后
local hotswap_funcs = {
    [HotSwapState.M0] = hs_state.hs_state_m0,
    [HotSwapState.M1] = hs_state.hs_state_m1,
    [HotSwapState.M2] = hs_state.hs_state_m2,
    [HotSwapState.M3] = hs_state.hs_state_m3,
    [HotSwapState.M4] = hs_state.hs_state_m4,
    [HotSwapState.M5] = hs_state.hs_state_m5,
    [HotSwapState.M6] = hs_state.hs_state_m6,
    [HotSwapState.M7] = hs_state.hs_state_m7
}

function hs_state:state_machine_run(event)
    -- 设置串口用户组为root，保证上下电信息正常打印
    utils_core.chown_s(UART_NAME, 0, 0)
    hotswap_funcs[self.state](self, event)
end

return hs_state
