---
source: framec/tests/gdscript_snapshots.rs
expression: "compile_fixture(\"08_lifecycle\", \"gdscript\")"
---
class LifecycleFrameEvent:
    var _message
    var _parameters

    func _init(message: String, parameters):
        self._message = message
        self._parameters = parameters


class LifecycleFrameContext:
    var event
    var _return
    var _data
    var _transitioned

    func _init(event: LifecycleFrameEvent, default_return):
        self.event = event
        self._return = default_return
        self._data = {}
        self._transitioned = false


class LifecycleCompartment:
    var state
    var state_args
    var state_vars
    var enter_args
    var exit_args
    var forward_event
    var parent_compartment

    func _init(state: String, parent_compartment = null):
        self.state = state
        self.state_args = []
        self.state_vars = {}
        self.enter_args = []
        self.exit_args = []
        self.forward_event = null
        self.parent_compartment = parent_compartment

    func copy() -> LifecycleCompartment:
        var c = LifecycleCompartment.new(self.state, self.parent_compartment)
        c.state_args = self.state_args.duplicate()
        c.state_vars = self.state_vars.duplicate()
        c.enter_args = self.enter_args.duplicate()
        c.exit_args = self.exit_args.duplicate()
        c.forward_event = self.forward_event
        return c


class Lifecycle:
    var _state_stack
    var __compartment
    var __next_compartment
    var _context_stack
    var entered = 0
    var exited = 0
    var tag = ""

    func _init():
        self._state_stack = []
        self._context_stack = []
        self.__compartment = self.__prepareEnter("Idle", [], [])
        self.__next_compartment = null

    static func _create():
        var c = Lifecycle.new()
        var __e = LifecycleFrameEvent.new("$>", c.__compartment.enter_args)
        var __ctx = LifecycleFrameContext.new(__e, null)
        c._context_stack.append(__ctx)
        c.__kernel(__e)
        c._context_stack.pop_back()
        return c

    func hsm_chain() -> Dictionary:
        return {
            "Idle": ["Idle"],
            "Running": ["Running"],
        }
    func __prepareEnter(leaf, state_args, enter_args):
        var comp = null
        for name in self.hsm_chain()[leaf]:
            var new_comp = LifecycleCompartment.new(name)
            new_comp.state_args = state_args.duplicate()
            new_comp.enter_args = enter_args.duplicate()
            new_comp.parent_compartment = comp
            comp = new_comp
        return comp

    func __prepareExit(exit_args):
        var comp = self.__compartment
        while comp != null:
            comp.exit_args = exit_args.duplicate()
            comp = comp.parent_compartment

    func __kernel(__e):
        # Route event to current state
        self.__router(__e)
        # Drain any transitions queued by the handler
        while self.__next_compartment != null:
            var next_compartment = self.__next_compartment
            self.__next_compartment = null
            # Exit the current (leaf) state
            self.__router(LifecycleFrameEvent.new("<$", self.__compartment.exit_args))
            # Switch to the new compartment
            self.__compartment = next_compartment
            if next_compartment.forward_event == null:
                # No forwarded event — synthesize a fresh $>
                self.__router(LifecycleFrameEvent.new("$>", self.__compartment.enter_args))
            else:
                if next_compartment.forward_event._message == "$>":
                    # Forwarded event IS $> — dispatch directly so the
                    # destination's $> receives the caller's payload
                    self.__router(next_compartment.forward_event)
                else:
                    # Forwarded event is not $> — initialize the destination
                    # with a fresh $>, then dispatch the forward to it
                    self.__router(LifecycleFrameEvent.new("$>", self.__compartment.enter_args))
                    self.__router(next_compartment.forward_event)
            next_compartment.forward_event = null
            # Mark all stacked contexts as transitioned
            for ctx in self._context_stack:
                ctx._transitioned = true

    func __router(__e):
        if self.__compartment.state == "Idle":
            self._state_Idle(__e, self.__compartment)
        elif self.__compartment.state == "Running":
            self._state_Running(__e, self.__compartment)

    func __transition(next_compartment):
        self.__next_compartment = next_compartment

    func start(label: String):
        var __e = LifecycleFrameEvent.new("start", [label])
        var __ctx = LifecycleFrameContext.new(__e, null)
        self._context_stack.append(__ctx)
        self.__kernel(__e)
        return self._context_stack.pop_back()._return

    func stop():
        var __e = LifecycleFrameEvent.new("stop", [])
        var __ctx = LifecycleFrameContext.new(__e, null)
        self._context_stack.append(__ctx)
        self.__kernel(__e)
        return self._context_stack.pop_back()._return

    func _state_Idle(__e, compartment):
        if __e._message == "start":
            self._s_Idle_hdl_user_start(__e, compartment)
            return

    func _state_Running(__e, compartment):
        if __e._message == "<$":
            self._s_Running_hdl_frame_exit(__e, compartment)
            return
        if __e._message == "$>":
            self._s_Running_hdl_frame_enter(__e, compartment)
            return
        if __e._message == "stop":
            self._s_Running_hdl_user_stop(__e, compartment)
            return

    func _s_Idle_hdl_user_start(__e, compartment):
        var label = __e._parameters[0]
        (label)
        var __compartment = self.__prepareEnter("Running", [], [])
        self.__transition(__compartment)
        return

    func _s_Running_hdl_frame_exit(__e, compartment):
        self.exited = self.exited + 1

    func _s_Running_hdl_frame_enter(__e, compartment):
        var label = compartment.enter_args[0]
        self.entered = self.entered + 1;
        self.tag = label;

    func _s_Running_hdl_user_stop(__e, compartment):
        var __compartment = self.__prepareEnter("Idle", [], [])
        self.__transition(__compartment)
        return
