from collections import deque
from threading import Condition, Lock
from typing import Deque

from rx.internal.priorityqueue import PriorityQueue

from .scheduleditem import ScheduledItem


class Trampoline(object):

    def __init__(self):
        self._idle: bool = True
        self._queue: PriorityQueue[ScheduledItem] = PriorityQueue()
        self._lock: Lock = Lock()
        self._condition: Condition = Condition(self._lock)

    def idle(self) -> bool:
        with self._lock:
            return self._idle

    def run(self, item: ScheduledItem) -> None:
        with self._lock:
            self._queue.enqueue(item)
            if self._idle:
                self._idle = False
            else:
                self._condition.notify()
                return
        try:
            self._run()
        finally:
            with self._lock:
                self._idle = True
                self._queue.clear()

    def _run(self) -> None:
        ready: Deque[ScheduledItem] = deque()
        while True:
            with self._lock:
                while len(self._queue) > 0:
                    item: ScheduledItem = self._queue.peek()
                    if item.duetime <= item.scheduler.now:
                        self._queue.dequeue()
                        ready.append(item)
                    else:
                        break

            while len(ready) > 0:
                item = ready.popleft()
                if not item.is_cancelled():
                    item.invoke()

            with self._lock:
                if len(self._queue) == 0:
                    break
                item = self._queue.peek()
                seconds = (item.duetime - item.scheduler.now).total_seconds()
                if seconds > 0.0:
                    self._condition.wait(seconds)
