# -*- coding: utf-8 -*-
from threading import Thread, Lock
import traceback
import functools

try:
    from queue import Queue
except ImportError:
    from Queue import Queue

class GenericListener(object):
    lock = Lock()

    def __init__(self):
        self.handlers = []
        self.listening = False
        self.queue = Queue()

    def invoke_handlers(self, event):
        for handler in self.handlers:
            try:
                if handler(event):
                    # Stop processing this hotkey.
                    return 1
            except Exception as e:
                traceback.print_exc()

    def start_if_necessary(self):
        """
        Starts the listening thread if it wans't already.
        """
        self.lock.acquire()
        try:
            if not self.listening:
                self.init()

                self.listening = True
                self.listening_thread = Thread(target=self.listen)
                self.listening_thread.daemon = True
                self.listening_thread.start()

                self.processing_thread = Thread(target=self.process)
                self.processing_thread.daemon = True
                self.processing_thread.start()
        finally:
            self.lock.release()

    def pre_process_event(self, event):
        raise NotImplementedError('This method should be implemented in the child class.')

    def process(self):
        """
        Loops over the underlying queue of events and processes them in order.
        """
        assert self.queue is not None
        while True:
            event = self.queue.get()
            if self.pre_process_event(event):
                self.invoke_handlers(event)
            self.queue.task_done()
            
    def add_handler(self, handler):
        """
        Adds a function to receive each event captured, starting the capturing
        process if necessary.
        """
        self.start_if_necessary()
        self.handlers.append(handler)

    def remove_handler(self, handler):
        """ Removes a previously added event handler. """
        self.handlers.remove(handler)
