# pylint: disable=unused-argument
from abc import ABC, abstractmethod
from itertools import takewhile
from vulk import eventconstant as ec
[docs]class RawEventListener(ABC):
    '''
    Base class for event listener.
    You shouldn't need to inherit directly from this class,
    it's very low level.
    '''
[docs]    @abstractmethod
    def handle(self, event):
        '''Called for each event received
        *Parameters:*
        - `event`: `eventconstant.BaseEvent`
        *Returns:*
        - `True` if event handled
        - `False` otherwise
        '''
        return False  
[docs]class EventChainListener(RawEventListener):
    '''
    Allow to chain events.
    When an event is sent to the `EventChain`, included event listeners
    will intercept events until one of them return False.
    '''
    def __init__(self, event_listeners=None):
        '''
        *Parameters:*
        - `event_listeners`: `list` of `RawEventListener`
        '''
        self.listeners = []
        if event_listeners:
            self.listeners.extend(event_listeners)
[docs]    def handle(self, event):
        '''
        Call event listener until one of them return False.
        *Parameters:*
        '''
        return any(list(takewhile(lambda x: x.handle(event), self.listeners)))  
[docs]class DispatchEventListener(RawEventListener):
    '''
    This class dispatch each event to its specific function.
    This class is very basic and performs no logic.
    To get more logic, you must use `BaseEventListener`.
    '''
[docs]    def handle(self, event):
        '''Called for each event received
        *Parameters:*
        - `event`: `eventconstant.BaseEvent`
        *Returns:*
        - `True` if event handled
        - `False` otherwise
        '''
        # Unknow event
        if not event:
            return False
        if event.type == ec.EventType.KEY_DOWN:
            return self.key_down(event.key)
        elif event.type == ec.EventType.KEY_UP:
            return self.key_up(event.key)
        elif event.type == ec.EventType.MOUSE_BUTTONUP:
            return self.mouse_up(event.x, event.y, event.button)
        elif event.type == ec.EventType.MOUSE_BUTTONDOWN:
            return self.mouse_down(event.x, event.y, event.button)
        elif event.type == ec.EventType.MOUSE_MOTION:
            return self.mouse_move(event.x, event.y, event.xr, event.yr)
        elif event.type == ec.EventType.QUIT:
            return self.quit()
        return False 
[docs]    def key_down(self, keycode):
        '''Called when a key is pressed
        *Parameters:*
        - `keycode`: `vulk.input.KeyCode`
        '''
        return False 
[docs]    def key_up(self, keycode):
        '''Called when a key is released
        *Parameters:*
        - `keycode`: `vulk.input.KeyCode`
        '''
        return False 
[docs]    def mouse_down(self, x, y, button):
        '''Called when mouse is released
        *Parameters:*
        - `x`: X position in Screen coordinate
        - `y`: Y position in Screen coordinate
        - `button`: `vulk.input.Button`
        '''
        return False 
[docs]    def mouse_up(self, x, y, button):
        '''Called when mouse is clicked
        *Parameters:*
        - `x`: X position in Screen coordinate
        - `y`: Y position in Screen coordinate
        - `button`: `vulk.input.Button`
        '''
        return False 
[docs]    def mouse_move(self, x, y, xr, yr):
        '''Called when mouse is moving
        *Parameters:*
        - `x`: X position in Screen coordinate
        - `y`: Y position in Screen coordinate
        - `xr`: X relative position since the last move
        - `yr`: Y relative position since the last move
        '''
        return False 
[docs]    def quit(self):
        '''Called when App must quit'''
        return False  
[docs]class BaseEventListener(DispatchEventListener):
    '''Extends `DispatchEventListener` with smarter functions'''
    def __init__(self):
        self.mouse_clicked = -1
[docs]    def mouse_down(self, x, y, button):
        self.mouse_clicked = button
        return False 
[docs]    def mouse_up(self, x, y, button):
        self.mouse_clicked = -1
        return False 
[docs]    def mouse_move(self, x, y, xr, yr):
        if self.mouse_clicked != -1:
            return self.mouse_drag(x, y, xr, yr, self.mouse_clicked)
        return False 
[docs]    def mouse_drag(self, x, y, xr, yr, button):
        '''Called when mouse is dragged
        *Parameters:*
        - `x`: X position in Screen coordinate
        - `y`: Y position in Screen coordinate
        - `xr`: X relative position since the last move
        - `yr`: Y relative position since the last move
        - `button`: `vulk.input.Button`
        '''
        return False  
[docs]def wrap_callback(f):
    '''
    Decorator used in `CallbackEventListener`.
    If a callback exists for the event, we call the callback, else we
    return False.
    '''
    def wrapper(self, *args):
        getattr(BaseEventListener, f.__name__)(self, *args)
        cb = getattr(self, f.__name__ + '_callback')
        if cb:
            return cb(*args)
        return False
    return wrapper 
[docs]class CallbackEventListener(BaseEventListener):
    '''
    Like `BaseEventListener` but with callback.
    You must pass named parameters with the exact same name as in
    `BaseEventListener`.
    *Example:*
    ```
    listener = CallbackEventListener(key_up=callback1, key_down=callback2)
    ```
    '''
    def __init__(self, key_down=None, key_up=None, mouse_down=None,
                 mouse_drag=None, mouse_move=None, mouse_up=None,
                 quit=None):  # pylint: disable=redefined-builtin
        # We use a custom dict instead of locals() to avoid black magic
        parameters = {
            'key_down': key_down,
            'key_up': key_up,
            'mouse_down': mouse_down,
            'mouse_drag': mouse_drag,
            'mouse_move': mouse_move,
            'mouse_up': mouse_up,
            'quit': quit
        }
        for key, value in parameters.items():
            setattr(self, key + '_callback', value)
        super().__init__()
[docs]    @wrap_callback
    def key_down(self, keycode):
        pass 
[docs]    @wrap_callback
    def key_up(self, keycode):
        pass 
[docs]    @wrap_callback
    def mouse_drag(self, x, y, xr, yr, button):
        pass 
[docs]    @wrap_callback
    def mouse_down(self, x, y, button):
        pass 
[docs]    @wrap_callback
    def mouse_up(self, x, y, button):
        pass 
[docs]    @wrap_callback
    def mouse_move(self, x, y, xr, yr):
        pass 
[docs]    @wrap_callback
    def quit(self):
        pass