from __future__ import unicode_literals

import logging
import pykka
import pyconnman
import dbus, gobject

from mopidy import service
from mopidy.utils.jsonrpc import private_method

logger = logging.getLogger(__name__)

CONNMAN_SERVICE_NAME = 'connman'

dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
gobject.threads_init()


def api_protect(f):
    """
    This decorator protects API calls by ensuring a valid
    pyconnman manager instance is running beforehand.  It
    will raise an exception if pyconnman manager is not
    ready. 
    """
    def wrapper(*args, **kwargs):
        if (args[0].manager is not None):
            return f(*args, **kwargs)
        else:
            raise Exception('Service not ready')
    return wrapper


def make_string(str):
    return dbus.String(str, variant_level=1)


def convert_dbus(obj):
    if (type(obj) is dbus.Byte):
        return int(obj)
    return obj


class ConnectionManager(pykka.ThreadingActor, service.Service):
    """
    ConnectionManager is a network connection manager service.

    The main use-case of this service is to allow the network settings
    to be configured through mopidy thus allowing new/existing WiFi
    networks to be joined or switch over to a LAN connection.
    """
    name = CONNMAN_SERVICE_NAME
    public = True
    agent_path = '/mopidy/wifi_agent'

    # Refer to https://pythonhosted.org/pyconnman for a description of
    # the configuration properties
    readonly_properties = ['State', 'Error', 'Name', 'Type', 'Security',
                           'Strength', 'Nameservers', 'Timeservers',
                           'Domains', 'IPv4', 'IPv6', 'Ethernet']
    readwrite_properties = ['Autoconnect', 'Nameservers.Configuration',
                            'Timeservers.Configuration', 'Domains.Configuration',
                            'IPv4.Configuration', 'IPv6.Configuration']

    def __init__(self, config, core):
        super(ConnectionManager, self).__init__()
        self.config = dict(config['connman'])
        self.manager = None
        self.agent = None

    def _services_changed_handler(self, signal, user_arg, changed, removed):
        """
        Helper to notify when the available connman connections
        has changed
        """
        if (changed):
            for i in changed:
                (_, props) = i
                if 'Name' in props:
                    ret_props = {}
                    for i in props.keys():
                        if (i in self.readonly_properties + self.readwrite_properties):
                            ret_props[i] = convert_dbus(props[i])
                    service.ServiceListener.send('connman_connection_changed', service=self.name,
                                                 connection=props.get('Name'), properties=ret_props)

    def _property_changed_handler(self, signal, user_arg, prop_name, prop_value):
        """
        Helper to notify when a connman property has changed
        """
        props = { prop_name: prop_value } 
        service.ServiceListener.send('connman_property_changed', service=self.name,
                                     properties=props)

    def _get_service_by_name(self, name):
        """
        Helper to find a service (aka connection) by its name
        and return its ConnService object
        """
        for (path, params) in self.manager.get_services():
            if (params.get('Name') == name):
                return pyconnman.ConnService(path)

    def _unregister_wifi_agent(self):
        """
        Helper to unregister a wifi agent if once is registered
        """
        try:
            if (self.agent):
                self.agent.remove_from_connection()
                self.manager.unregister_agent(self.agent_path)
                self.agent = None
        except:
            pass

    @private_method
    def on_start(self):
        """
        Activate the service
        """
        if (self.manager):
            return

        # Create connman manager
        manager = pyconnman.ConnManager()
        manager.add_signal_receiver(self._services_changed_handler,
                                    pyconnman.ConnManager.SIGNAL_SERVICES_CHANGED,
                                    None)
        manager.add_signal_receiver(self._property_changed_handler,
                                    pyconnman.ConnManager.SIGNAL_PROPERTY_CHANGED,
                                    None)
        self.manager = manager

        # Create agent for authenticating WiFi connections
        self._unregister_wifi_agent()
        self.agent = pyconnman.SimpleWifiAgent(self.agent_path)
        self.manager.register_agent(self.agent_path)

        # Enable the services listed in default configuration -
        # anything listed is powered on.  Otherwise it is
        # powered off.
        for (path,_) in self.manager.get_technologies():
            tech = pyconnman.ConnTechnology(path)
            if (tech.Type in self.config['powered'] and not tech.Powered):
                tech.Powered = True