
from __future__ import absolute_import
import logging

log = logging.getLogger("storage.asyncutils")


class LoopingCall(object):

    warning_delay = 0.5

    def __init__(self, loop, callback, *args):
        self._loop = loop
        self._callback = callback
        self._args = args
        self._running = False
        self._deadline = None
        self._interval = None
        self._timer = None

    def start(self, interval):
        assert not self._running, "LoopingCall is already running"
        self._running = True
        self._interval = interval
        self._deadline = self._loop.time()
        self()

    def is_running(self):
        return self._running

    @property
    def deadline(self):
        return self._deadline

    def stop(self):
        if self._running:
            self._running = False
        if self._timer:
            self._timer.cancel()
            self._timer = None

    def __call__(self):
        if not self._running:
            return
        delay = self._loop.time() - self._deadline
        if delay > self.warning_delay:
            log.warning("Call %s delayed by %.2f seconds",
                        self._callback, delay)
        try:
            self._callback(*self._args)
        finally:
            if self._running:
                self._schedule_next_call()

    def _schedule_next_call(self):
        self._deadline += self._interval
        now = self._loop.time()
        if self._deadline <= now:
            missed = (now - self._deadline) // self._interval + 1
            self._deadline += missed * self._interval
            log.warning("Call %s missed %d deadlines, scheduling next call "
                        "at %.2f",
                        self._callback, missed, self._deadline)
        self._timer = self._loop.call_at(self._deadline, self)
