import sys
import eventlet
import contexter
import six


class Task(object):
    @classmethod
    def get_or_create(cls, signal, kwargs=None, logger=None):
        if not hasattr(cls, '_registry'):
            cls._registry = []

        task = cls(signal, kwargs, logger=logger)

        if task not in cls._registry:
            cls._registry.append(task)

        return cls._registry[cls._registry.index(task)]

    def __init__(self, signal, kwargs=None, logger=None):
        self.signal = signal
        self.kwargs = kwargs or {}
        self.logger = logger
        self.failures = 0
        self.task_semaphore = eventlet.semaphore.BoundedSemaphore(1)

    def __call__(self, semaphores=None):
        semaphores = semaphores or []

        with contexter.Contexter(self.task_semaphore, *semaphores):
            result = self._do()

        if result:
            self.failures = 0
        else:
            self.failures += 1

        return result

    def _do(self):
        try:
            self._emit()
        except Exception:
            self._exception(*sys.exc_info())
            return False
        else:
            self._completed()
            return True
        finally:
            self._clean()

    def _clean(self):
        pass

    def _completed(self):
        if self.logger:
            self.logger.info('[%s] Completed' % self)

    def _exception(self, e_type, e_value, e_traceback):
        if self.logger:
            self.logger.exception('[%s] Raised exception: %s' % (
                self, e_value))
        else:
            six.reraise(e_type, e_value, e_traceback)

    def _emit(self):
        if self.logger:
            self.logger.info('[%s] Running' % self)
        self.signal.emit(**self.kwargs)

    def __eq__(self, other):
        return (self.signal == other.signal and self.kwargs == other.kwargs)

    def __str__(self):
        return '%s: %s' % (self.signal.__class__.__name__, self.kwargs)