---
source: framec/tests/python_snapshots.rs
expression: "compile_fixture(\"02_hsm\", \"python_3\")"
---
from typing import Any, Optional, List, Dict, Callable

class MiniHsmFrameEvent:
    def __init__(self, message: str, parameters):
        self._message = message
        self._parameters = parameters


class MiniHsmFrameContext:
    def __init__(self, event: MiniHsmFrameEvent, default_return):
        self.event = event
        self._return = default_return
        self._data = {}
        self._transitioned = False


class MiniHsmCompartment:
    def __init__(self, state: str, parent_compartment = None):
        self.state = state
        self.state_args = []
        self.state_vars = {}
        self.enter_args = []
        self.exit_args = []
        self.forward_event = None
        self.parent_compartment = parent_compartment

    def copy(self) -> 'MiniHsmCompartment':
        c = MiniHsmCompartment(self.state, self.parent_compartment)
        c.state_args = self.state_args.copy()
        c.state_vars = self.state_vars.copy()
        c.enter_args = self.enter_args.copy()
        c.exit_args = self.exit_args.copy()
        c.forward_event = self.forward_event
        return c


class MiniHsm:
    def __init__(self):
        self._state_stack = []
        self._context_stack = []
        self.awakes = 0
        self.sleeps = 0
        self.last = 0
        self.__compartment = self.__prepareEnter("Live", [], [])
        self.__next_compartment = None

    @classmethod
    def _create(cls):
        c = cls()
        __e = MiniHsmFrameEvent("$>", c.__compartment.enter_args)
        __ctx = MiniHsmFrameContext(__e, None)
        c._context_stack.append(__ctx)
        c.__kernel(__e)
        c._context_stack.pop()
        return c

    _HSM_CHAIN = {
        "Live": ["Live"],
        "Awake": ["Live", "Awake"],
        "Asleep": ["Live", "Asleep"],
    }
    def __prepareEnter(self, leaf, state_args, enter_args):
        comp = None
        for name in self._HSM_CHAIN[leaf]:
            new_comp = MiniHsmCompartment(name)
            new_comp.state_args = list(state_args)
            new_comp.enter_args = list(enter_args)
            new_comp.parent_compartment = comp
            comp = new_comp
        return comp

    def __prepareExit(self, exit_args):
        comp = self.__compartment
        while comp is not None:
            comp.exit_args = list(exit_args)
            comp = comp.parent_compartment

    def __kernel(self, __e):
        # Route event to current state
        self.__router(__e)
        # Drain any transitions queued by the handler
        while self.__next_compartment is not None:
            next_compartment = self.__next_compartment
            self.__next_compartment = None
            # Exit the current (leaf) state
            self.__router(MiniHsmFrameEvent("<$", self.__compartment.exit_args))
            # Switch to the new compartment
            self.__compartment = next_compartment
            if next_compartment.forward_event is None:
                # No forwarded event — synthesize a fresh $>
                self.__router(MiniHsmFrameEvent("$>", 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(MiniHsmFrameEvent("$>", self.__compartment.enter_args))
                    self.__router(next_compartment.forward_event)
            next_compartment.forward_event = None
            # Mark all stacked contexts as transitioned
            for ctx in self._context_stack:
                ctx._transitioned = True

    def __router(self, __e):
        if self.__compartment.state == "Live":
            self._state_Live(__e, self.__compartment)
        elif self.__compartment.state == "Awake":
            self._state_Awake(__e, self.__compartment)
        elif self.__compartment.state == "Asleep":
            self._state_Asleep(__e, self.__compartment)

    def __transition(self, next_compartment):
        self.__next_compartment = next_compartment

    def wake(self):
        __e = MiniHsmFrameEvent("wake", [])
        __ctx = MiniHsmFrameContext(__e, None)
        self._context_stack.append(__ctx)
        try:
            self.__kernel(__e)
        finally:
            __frame_ctx = self._context_stack.pop()
        return __frame_ctx._return

    def sleep(self):
        __e = MiniHsmFrameEvent("sleep", [])
        __ctx = MiniHsmFrameContext(__e, None)
        self._context_stack.append(__ctx)
        try:
            self.__kernel(__e)
        finally:
            __frame_ctx = self._context_stack.pop()
        return __frame_ctx._return

    def signal(self):
        __e = MiniHsmFrameEvent("signal", [])
        __ctx = MiniHsmFrameContext(__e, None)
        self._context_stack.append(__ctx)
        try:
            self.__kernel(__e)
        finally:
            __frame_ctx = self._context_stack.pop()
        return __frame_ctx._return

    def _state_Live(self, __e, compartment):
        if __e._message == "signal":
            self._s_Live_hdl_user_signal(__e, compartment)
            return
        if __e._message == "sleep":
            self._s_Live_hdl_user_sleep(__e, compartment)
            return
        if __e._message == "wake":
            self._s_Live_hdl_user_wake(__e, compartment)
            return

    def _state_Awake(self, __e, compartment):
        if __e._message == "$>":
            self._s_Awake_hdl_frame_enter(__e, compartment)
            return
        if __e._message == "signal":
            self._s_Awake_hdl_user_signal(__e, compartment)
            return

    def _state_Asleep(self, __e, compartment):
        if __e._message == "$>":
            self._s_Asleep_hdl_frame_enter(__e, compartment)
            return
        if __e._message == "signal":
            self._s_Asleep_hdl_user_signal(__e, compartment)
            return
        if __e._message == "wake":
            self._s_Asleep_hdl_user_wake(__e, compartment)
            return

    def _s_Live_hdl_user_signal(self, __e, compartment):
        pass

    def _s_Live_hdl_user_sleep(self, __e, compartment):
        pass

    def _s_Live_hdl_user_wake(self, __e, compartment):
        pass

    def _s_Awake_hdl_frame_enter(self, __e, compartment):
        self.awakes = self.awakes + 1

    def _s_Awake_hdl_user_signal(self, __e, compartment):
        self.last = 1
        self._state_Live(__e, compartment.parent_compartment)

    def _s_Asleep_hdl_frame_enter(self, __e, compartment):
        self.sleeps = self.sleeps + 1

    def _s_Asleep_hdl_user_signal(self, __e, compartment):
        self.last = 2
        self._state_Live(__e, compartment.parent_compartment)

    def _s_Asleep_hdl_user_wake(self, __e, compartment):
        __compartment = self.__prepareEnter("Awake", [], [])
        self.__transition(__compartment)
        return
