# -*- coding: utf-8 -*-
# Moovida - Home multimedia server
# Copyright (C) 2006-2009 Fluendo Embedded S.L. (www.fluendo.com).
# All rights reserved.
#
# This file is available under one of two license agreements.
#
# This file is licensed under the GPL version 3.
# See "LICENSE.GPL" in the root of this distribution including a special
# exception to use Moovida with Fluendo's plugins.
#
# The GPL part of Moovida is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Moovida" in the root directory of this distribution package
# for details on that license.
#
# Author: Florian Boucault <florian@fluendo.com>

from elisa.core.manager import Manager
from elisa.core.components.input_provider import PushInputProvider, \
                                                 PollInputProvider

from twisted.internet import task

import gobject, warnings


class InputManager(Manager, gobject.GObject):
    """
    InputManager provides a common place to retrieve input events
    coming from a GUI toolkit, additional input sources or even a
    network. InputEvents can be pushed by
    L{elisa.core.components.input_provider.InputProvider}s
    or be polled by the InputManager, depending on the implementation
    chosen: L{elisa.core.components.input_provider.PollInputProvider}
    or L{elisa.core.components.input_provider.PushInputProvider}.

    Other objects can connect to the manager's signals
    that are emitted when L{elisa.core.input_event.InputEvent}s coming from
    L{elisa.core.components.input_provider.InputProvider}s are received.
    """

    __gsignals__ = {
                    'input_event' : (gobject.SIGNAL_RUN_LAST,
                                     gobject.TYPE_NONE,
                                     (object,))
                    }

    entry_point = 'elisa.core.components.input_provider'

    def __init__(self):
        super(InputManager, self).__init__()

        # 35Hz polling rate for the PollInputProvider
        self._poll_rate = 1 / 35.
        self._poll_call = task.LoopingCall(self._poll_events)

    def clean(self):
        if self._poll_call.running:
            self._poll_call.stop()

        return super(InputManager, self).clean()

    def process_event(self, event):
        """ Fire the signal corresponding to the event.

        Each event type is mapped to a signal instance to which other
        elisa components can connect (e.g to monitor user key presses).

        This method can be called by
        L{elisa.core.components.input_provider.PushInputProvider}
        components when they receive input data from the input device.

        @param event:         the event to process
        @type event:          L{elisa.core.input_event.InputEvent}
        """
        self.debug("Event received: %r", event)

        if event == None:
            return

        self.emit('input_event', event)

    def register_component(self, component):
        """ Register a new InputProvider in the InputManager so that the
        events collected by the former are propagated by the latter.

        @param component: the InputProvider instance to register
        @type component:  L{elisa.core.components.input_provider.InputProvider}
        """
        dfr = super(InputManager, self).register_component(component)
        dfr.addCallback(self._bind_component, component)
        return dfr

    def unregister_component(self, component):
        """ Clean the InputProvider and unregister it from the InputManager;
        no events from the InputProvider will be propagated anymore.

        @param component: the InputProvider instance to unregister
        @type component:  L{elisa.core.components.input_provider.InputProvider}
        """
        dfr = super(InputManager, self).unregister_component(component)
        dfr.addCallback(self._unbind_component, component)
        return dfr

    def _bind_component(self, result, component):
        if isinstance(component, PollInputProvider):
            warnings.warn("PollInputProviders are deprecated",
                    DeprecationWarning)
            self._check_polling()
            return

        component.input_manager = self
        component.bind()

    def _unbind_component(self, result, component):
        if isinstance(component, PushInputProvider):
            component.unbind()
        else:
            self._check_polling()

    def _check_polling(self):
        have_poll = False
        for component in self.components:
            if isinstance(component, PollInputProvider):
                have_poll = True
                break

        if have_poll and not self._poll_call.running:
            self._poll_call.start(self._poll_rate)
        elif self._poll_call.running:
            self._poll_call.stop()

    def _poll_events(self):
        """ Poll each registered PollInputProvider for InputEvents to
        process.
        """
        for component in self.components:
            if not isinstance(component, PollInputProvider):
                continue

            events = component.get_input_events()
            for event in events:
                self.process_event(event)

gobject.type_register(InputManager)
