class SyncdaemonService(SyncdaemonObject):
    """The main service."""

    def __init__(self, main, send_events=False, interface=None):
        super(SyncdaemonService, self).__init__(main, interface)

        self.send_events = send_events
        self.network_manager = NetworkManagerState(
            result_cb=self.network_state_changed)
        self.network_manager.find_online_state()

        if interface is None:
            self.interface = ExternalInterface(service=self)
        else:
            self.interface = interface

        self._create_children()

        self.main.event_q.subscribe(self.event_listener)
        self.all_events_sender = None
        if self.send_events:
            self.all_events_sender = AllEventsSender(self.interface.events)
            self.main.event_q.subscribe(self.all_events_sender)

        self.auth_credentials = None

    def _create_children(self):
        """Create the specific syncdaemon objects."""
        self.status = SyncdaemonStatus(self.main, self.interface)
        self.file_system = SyncdaemonFileSystem(self.main, self.interface)
        self.shares = SyncdaemonShares(self.main, self.interface)
        self.config = SyncdaemonConfig(self.main, self.interface)
        self.folders = SyncdaemonFolders(self.main, self.interface)
        self.public_files = SyncdaemonPublicFiles(self.main, self.interface)
        self.events = SyncdaemonEvents(self.main, self.interface)
        self.event_listener = SyncdaemonEventListener(self.main,
                                                      self.interface)
        self.sync = self  # for API compatibility

    @log_call(logger.info)
    def start(self):
        """Start listening for ipc messages."""
        return self.interface.start()

    @log_call(logger.info)
    def shutdown(self, with_restart=False):
        """Shutdown the interface and unsubscribe from the event queue."""
        self.main.event_q.unsubscribe(self.event_listener)
        if self.send_events:
            self.main.event_q.unsubscribe(self.all_events_sender)

        self.interface.shutdown(with_restart=with_restart)

    @log_call(logger.info)
    def connect(self, autoconnecting=True):
        """Push the SYS_USER_CONNECT event with the stored credentials.

        If 'autoconnecting' is False, nothing will be done.

        """
        d = defer.succeed(None)

        if not autoconnecting:
            logger.info('connect: autoconnecting not set, doing nothing.')
            return d

        if self.auth_credentials is None:
            logger.error('connect: autoconnecting set but no credentials.')
            return defer.fail(NoAccessToken("got empty credentials."))

        logger.debug('connect: auth credentials were given by parameter.')
        self.main.event_q.push(
            'SYS_USER_CONNECT', access_token=self.auth_credentials)

        return d

    @log_call(logger.debug)
    def disconnect(self):
        """Disconnect from the server."""
        self.main.event_q.push('SYS_USER_DISCONNECT')

    @log_call(logger.debug)
    def get_homedir(self):
        """Return the home dir point."""
        return self.main.get_homedir().decode('utf-8')

    @log_call(logger.debug)
    def get_rootdir(self):
        """Return the root dir/mount point."""
        return self.main.get_rootdir().decode('utf-8')

    @log_call(logger.debug)
    def get_sharesdir(self):
        """Return the shares dir/mount point."""
        return self.main.get_sharesdir().decode('utf-8')

    @log_call(logger.debug)
    def get_sharesdir_link(self):
        """Return the shares dir/mount point."""
        return self.main.get_sharesdir_link().decode('utf-8')

    @log_call(logger.debug)
    def wait_for_nirvana(self, last_event_interval):
        """Return a deferred that will be fired when nirvana is reached.

        Nirvana means there are no more events/transfers.

        """
        return self.main.wait_for_nirvana(last_event_interval)

    @log_call(logger.debug)
    def quit(self):
        """Shutdown the syncdaemon."""
        return self.main.quit()

    @unicode_to_bytes
    @log_call(logger.debug)
    def rescan_from_scratch(self, volume_id):
        """Request a rescan from scratch of the volume with volume_id."""
        # check that the volume exists
        volume = self.main.vm.get_volume(volume_id)
        self.main.action_q.rescan_from_scratch(volume.volume_id)

    @log_call(logger.debug)
    def network_state_changed(self, state):
        """Receive the connection state and call the proper function."""
        if state == ONLINE:
            self.network_connected()
        else:
            self.network_disconnected()

    @log_call(logger.debug)
    def network_connected(self):
        """Push the connected event."""
        self.main.event_q.push('SYS_NET_CONNECTED')

    @log_call(logger.debug)
    def network_disconnected(self):
        """Push the disconnected event."""
        self.main.event_q.push('SYS_NET_DISCONNECTED')
class SyncdaemonService(SyncdaemonObject):
    """The main service."""

    def __init__(self, main, send_events=False, interface=None):
        super(SyncdaemonService, self).__init__(main, interface)

        self.send_events = send_events
        self.network_manager = NetworkManagerState(
            result_cb=self.network_state_changed)
        self.network_manager.find_online_state()

        if interface is None:
            self.interface = ExternalInterface(service=self)
        else:
            self.interface = interface

        self._create_children()

        self.main.event_q.subscribe(self.event_listener)
        self.all_events_sender = None
        if self.send_events:
            self.all_events_sender = AllEventsSender(self.interface.events)
            self.main.event_q.subscribe(self.all_events_sender)

        self.auth_credentials = None

    def _create_children(self):
        """Create the specific syncdaemon objects."""
        self.status = SyncdaemonStatus(self.main, self.interface)
        self.file_system = SyncdaemonFileSystem(self.main, self.interface)
        self.shares = SyncdaemonShares(self.main, self.interface)
        self.config = SyncdaemonConfig(self.main, self.interface)
        self.folders = SyncdaemonFolders(self.main, self.interface)
        self.public_files = SyncdaemonPublicFiles(self.main, self.interface)
        self.events = SyncdaemonEvents(self.main, self.interface)
        self.event_listener = SyncdaemonEventListener(self.main,
                                                      self.interface)
        self.sync = self  # for API compatibility

    @log_call(logger.info)
    def start(self):
        """Start listening for ipc messages."""
        return self.interface.start()

    @log_call(logger.info)
    def shutdown(self, with_restart=False):
        """Shutdown the interface and unsubscribe from the event queue."""
        self.main.event_q.unsubscribe(self.event_listener)
        if self.send_events:
            self.main.event_q.unsubscribe(self.all_events_sender)

        self.interface.shutdown(with_restart=with_restart)

    @defer.inlineCallbacks
    @log_call(logger.info)
    def connect(self, autoconnecting=False):
        """Push the SYS_USER_CONNECT event with the token.

        The token is requested via com.ubuntuone.credentials service. If
        'autoconnecting' is True, no UI window will be raised to prompt the
        user for login/registration, only already existent credentials will be
        used.

        """
        if self.auth_credentials is not None:
            logger.debug('connect: auth credentials were given by parameter.')
            token = self.auth_credentials
        else:
            try:
                token = yield self._request_token(
                    autoconnecting=autoconnecting)
            except Exception as e:
                logger.exception('failure while getting the token')
                raise NoAccessToken(e)

            if not token:
                raise NoAccessToken("got empty credentials.")

        self.main.event_q.push('SYS_USER_CONNECT', access_token=token)

    def _request_token(self, autoconnecting):
        """Request to SSO auth service to fetch the token."""
        # FIXME: we need to unbind this from SSO, probably just
        # get tokens from keyring
        # call ubuntu sso
        management = credentials.CredentialsManagementTool()
        # return the deferred, since we are no longer using signals
        if autoconnecting:
            return management.find_credentials()
        else:
            return management.login(window_id=0)  # no window ID

    @log_call(logger.debug)
    def disconnect(self):
        """Disconnect from the server."""
        self.main.event_q.push('SYS_USER_DISCONNECT')

    @log_call(logger.debug)
    def get_homedir(self):
        """Return the home dir point."""
        return self.main.get_homedir().decode('utf-8')

    @log_call(logger.debug)
    def get_rootdir(self):
        """Return the root dir/mount point."""
        return self.main.get_rootdir().decode('utf-8')

    @log_call(logger.debug)
    def get_sharesdir(self):
        """Return the shares dir/mount point."""
        return self.main.get_sharesdir().decode('utf-8')

    @log_call(logger.debug)
    def get_sharesdir_link(self):
        """Return the shares dir/mount point."""
        return self.main.get_sharesdir_link().decode('utf-8')

    @log_call(logger.debug)
    def wait_for_nirvana(self, last_event_interval):
        """Return a deferred that will be fired when nirvana is reached.

        Nirvana means there are no more events/transfers.

        """
        return self.main.wait_for_nirvana(last_event_interval)

    @log_call(logger.debug)
    def quit(self):
        """Shutdown the syncdaemon."""
        return self.main.quit()

    @unicode_to_bytes
    @log_call(logger.debug)
    def rescan_from_scratch(self, volume_id):
        """Request a rescan from scratch of the volume with volume_id."""
        # check that the volume exists
        volume = self.main.vm.get_volume(volume_id)
        self.main.action_q.rescan_from_scratch(volume.volume_id)

    @log_call(logger.debug)
    def network_state_changed(self, state):
        """Receive the connection state and call the proper function."""
        if state == ONLINE:
            self.network_connected()
        else:
            self.network_disconnected()

    @log_call(logger.debug)
    def network_connected(self):
        """Push the connected event."""
        self.main.event_q.push('SYS_NET_CONNECTED')

    @log_call(logger.debug)
    def network_disconnected(self):
        """Push the disconnected event."""
        self.main.event_q.push('SYS_NET_DISCONNECTED')