# Path: tomate/pomodoro/event.py
# class Bus:
#     def __init__(self):
#         self._bus = blinker.NamedSignal("tomate")
# 
#     def connect(self, event: Events, receiver: Receiver, weak: bool = True):
#         self._bus.connect(receiver, sender=event, weak=weak)
# 
#     def is_connect(self, event: Events, receiver: Receiver) -> bool:
#         return receiver in self._bus.receivers_for(event)
# 
#     def send(self, event: Events, payload: Any = None) -> List[Any]:
#         # drop receiver, index 0, from the result
#         return [result[1] for result in self._bus.send(event, payload=payload)]
# 
#     def disconnect(self, event: Events, receiver: Receiver):
#         self._bus.disconnect(receiver, sender=event)
# 
# class Events(enum.Enum):
#     TIMER_START = 0
#     TIMER_UPDATE = 1
#     TIMER_STOP = 2
#     TIMER_END = 3
# 
#     SESSION_READY = 4
#     SESSION_START = 5
#     SESSION_INTERRUPT = 6
#     SESSION_CHANGE = 7
#     SESSION_END = 8
#     SESSION_RESET = 9
# 
#     WINDOW_SHOW = 10
#     WINDOW_HIDE = 11
# 
#     CONFIG_CHANGE = 12
# 
# Path: tomate/pomodoro/fsm.py
# class fsm:
#     def __init__(self, target, **kwargs):
#         self.target = target
#         self.source = kwargs.pop("source", "*")
#         self.attr = kwargs.pop("attr", "state")
#         self.condition = kwargs.pop("condition", None)
#         self.exit_action = kwargs.pop("exit", None)
# 
#     def is_valid_transition(self, instance) -> bool:
#         return self.source == "*" or getattr(instance, self.attr) in self.source
# 
#     def is_valid_condition(self, instance) -> bool:
#         if not self.condition:
#             return True
# 
#         return self.condition(instance)
# 
#     def change_state(self, instance) -> None:
#         current_target = getattr(instance, self.attr, None)
# 
#         logger.debug(
#             "action=change attribute=%s.%s from=%s to=%s",
#             instance.__class__.__name__,
#             self.attr,
#             current_target,
#             self.target,
#         )
# 
#         if self.target != "self" and current_target != self.target:
#             setattr(instance, self.attr, self.target)
# 
#     def call_exit_action(self, instance) -> None:
#         if self.exit_action is not None:
#             self.exit_action(instance)
# 
#     @wrapt.decorator
#     def __call__(self, wrapped, instance, args, kwargs):
#         # when there is a decorator over the @fsm the instance will be None so we need to get it from the first param
#         if instance is None:
#             instance = args[0]
# 
#         logger.debug(
#             "action=before method=%s.%s",
#             instance.__class__.__name__,
#             wrapped.__name__,
#         )
# 
#         if self.is_valid_transition(instance) and self.is_valid_condition(instance):
#             result = wrapped(*args, **kwargs)
#             self.change_state(instance)
#             self.call_exit_action(instance)
# 
#             logger.debug(
#                 "action=after method=%s.%s called=true",
#                 instance.__class__.__name__,
#                 wrapped.__name__,
#             )
# 
#             return result
# 
#         logger.debug(
#             "action=after method=%s.%s called=false transition=%s",
#             instance.__class__.__name__,
#             wrapped.__name__,
#             self.is_valid_transition(instance),
#         )
# 
#         return False


import enum
import logging
from collections import namedtuple
from gi.repository import GLib
from wiring import SingletonScope, inject
from wiring.scanning import register
from .event import Bus, Events
from .fsm import fsm

@register.factory("tomate.timer", scope=SingletonScope)
class Timer:
    ONE_SECOND = 1

    @inject(bus="tomate.bus")
    def __init__(self, bus: Bus):
        self.duration = self.time_left = 0
        self.state = State.STOPPED
        self._bus = bus

    @fsm(target=State.STARTED, source=[State.ENDED, State.STOPPED], exit=lambda self: self._trigger(Events.TIMER_START))
    def start(self, seconds: int) -> bool:
        logger.debug("action=start")
        self.duration = self.time_left = seconds
        GLib.timeout_add_seconds(Timer.ONE_SECOND, self._update, priority=GLib.PRIORITY_HIGH)
        return True

    @fsm(target=State.STOPPED, source=[State.STARTED], exit=lambda self: self._trigger(Events.TIMER_STOP))
    def stop(self) -> bool:
        logger.debug("action=reset")
        self._reset()
        return True

    def _is_up(self) -> bool:
        return self.time_left <= 0

    def is_running(self) -> bool:
        return self.state == State.STARTED


# Path: tomate/pomodoro/event.py
# class Bus:
#     def __init__(self):
#         self._bus = blinker.NamedSignal("tomate")
# 
#     def connect(self, event: Events, receiver: Receiver, weak: bool = True):
#         self._bus.connect(receiver, sender=event, weak=weak)
# 
#     def is_connect(self, event: Events, receiver: Receiver) -> bool:
#         return receiver in self._bus.receivers_for(event)
# 
#     def send(self, event: Events, payload: Any = None) -> List[Any]:
#         # drop receiver, index 0, from the result
#         return [result[1] for result in self._bus.send(event, payload=payload)]
# 
#     def disconnect(self, event: Events, receiver: Receiver):
#         self._bus.disconnect(receiver, sender=event)
# 
# class Events(enum.Enum):
#     TIMER_START = 0
#     TIMER_UPDATE = 1
#     TIMER_STOP = 2
#     TIMER_END = 3
# 
#     SESSION_READY = 4
#     SESSION_START = 5
#     SESSION_INTERRUPT = 6
#     SESSION_CHANGE = 7
#     SESSION_END = 8
#     SESSION_RESET = 9
# 
#     WINDOW_SHOW = 10
#     WINDOW_HIDE = 11
# 
#     CONFIG_CHANGE = 12
# 
# Path: tomate/pomodoro/fsm.py
# class fsm:
#     def __init__(self, target, **kwargs):
#         self.target = target
#         self.source = kwargs.pop("source", "*")
#         self.attr = kwargs.pop("attr", "state")
#         self.condition = kwargs.pop("condition", None)
#         self.exit_action = kwargs.pop("exit", None)
# 
#     def is_valid_transition(self, instance) -> bool:
#         return self.source == "*" or getattr(instance, self.attr) in self.source
# 
#     def is_valid_condition(self, instance) -> bool:
#         if not self.condition:
#             return True
# 
#         return self.condition(instance)
# 
#     def change_state(self, instance) -> None:
#         current_target = getattr(instance, self.attr, None)
# 
#         logger.debug(
#             "action=change attribute=%s.%s from=%s to=%s",
#             instance.__class__.__name__,
#             self.attr,
#             current_target,
#             self.target,
#         )
# 
#         if self.target != "self" and current_target != self.target:
#             setattr(instance, self.attr, self.target)
# 
#     def call_exit_action(self, instance) -> None:
#         if self.exit_action is not None:
#             self.exit_action(instance)
# 
#     @wrapt.decorator
#     def __call__(self, wrapped, instance, args, kwargs):
#         # when there is a decorator over the @fsm the instance will be None so we need to get it from the first param
#         if instance is None:
#             instance = args[0]
# 
#         logger.debug(
#             "action=before method=%s.%s",
#             instance.__class__.__name__,
#             wrapped.__name__,
#         )
# 
#         if self.is_valid_transition(instance) and self.is_valid_condition(instance):
#             result = wrapped(*args, **kwargs)
#             self.change_state(instance)
#             self.call_exit_action(instance)
# 
#             logger.debug(
#                 "action=after method=%s.%s called=true",
#                 instance.__class__.__name__,
#                 wrapped.__name__,
#             )
# 
#             return result
# 
#         logger.debug(
#             "action=after method=%s.%s called=false transition=%s",
#             instance.__class__.__name__,
#             wrapped.__name__,
#             self.is_valid_transition(instance),
#         )
# 
#         return False


# Path: tomate/pomodoro/timer.py
import enum
import logging
from collections import namedtuple
from gi.repository import GLib
from wiring import SingletonScope, inject
from wiring.scanning import register
from .event import Bus, Events
from .fsm import fsm


@register.factory("tomate.timer", scope=SingletonScope)
class Timer:
    ONE_SECOND = 1

    @inject(bus="tomate.bus")
    def __init__(self, bus: Bus):
        self.duration = self.time_left = 0
        self.state = State.STOPPED
        self._bus = bus

    @fsm(target=State.STARTED, source=[State.ENDED, State.STOPPED], exit=lambda self: self._trigger(Events.TIMER_START))
    def start(self, seconds: int) -> bool:
        logger.debug("action=start")
        self.duration = self.time_left = seconds
        GLib.timeout_add_seconds(Timer.ONE_SECOND, self._update, priority=GLib.PRIORITY_HIGH)
        return True

    @fsm(target=State.STOPPED, source=[State.STARTED], exit=lambda self: self._trigger(Events.TIMER_STOP))
    def stop(self) -> bool:
        logger.debug("action=reset")
        self._reset()
        return True

    def _is_up(self) -> bool:
        return self.time_left <= 0

    def is_running(self) -> bool:
        return self.state == State.STARTED


    @fsm(