import logging
import os
import threading

from spotify.manager import SpotifySessionManager as PyspotifySessionManager

from pykka.registry import ActorRegistry

from mopidy import get_version, settings
from mopidy.backends.spotify.translator import SpotifyTranslator
from mopidy.models import Playlist
from mopidy.outputs.base import BaseOutput
from mopidy.utils.process import BaseThread

logger = logging.getLogger('mopidy.backends.spotify.session_manager')

# pylint: disable = R0901
# SpotifySessionManager: Too many ancestors (9/7)

class SpotifySessionManager(BaseThread, PyspotifySessionManager):
    cache_location = settings.SPOTIFY_CACHE_PATH
    settings_location = settings.SPOTIFY_CACHE_PATH
    appkey_file = os.path.join(os.path.dirname(__file__), 'spotify_appkey.key')
    user_agent = 'Mopidy %s' % get_version()

    def __init__(self, username, password):
        PyspotifySessionManager.__init__(self, username, password)
        BaseThread.__init__(self)
        self.name = 'SpotifySMThread'

        output_refs = ActorRegistry.get_by_class(BaseOutput)
        assert len(output_refs) == 1, 'Expected exactly one running output.'
        self.output = output_refs[0].proxy()

        self.connected = threading.Event()
        self.session = None

    def run_inside_try(self):
        self.connect()

    def logged_in(self, session, error):
        """Callback used by pyspotify"""
        logger.info(u'Connected to Spotify')
        self.session = session
        if settings.SPOTIFY_HIGH_BITRATE:
            logger.debug(u'Preferring high bitrate from Spotify')
            self.session.set_preferred_bitrate(1)
        else:
            logger.debug(u'Preferring normal bitrate from Spotify')
            self.session.set_preferred_bitrate(0)
        self.connected.set()

    def logged_out(self, session):
        """Callback used by pyspotify"""
        logger.info(u'Disconnected from Spotify')

    def metadata_updated(self, session):
        """Callback used by pyspotify"""
        logger.debug(u'Metadata updated')
        self.refresh_stored_playlists()

    def connection_error(self, session, error):
        """Callback used by pyspotify"""
        logger.error(u'Connection error: %s', error)

    def message_to_user(self, session, message):
        """Callback used by pyspotify"""
        logger.debug(u'User message: %s', message.strip())

    def notify_main_thread(self, session):
        """Callback used by pyspotify"""
        logger.debug(u'notify_main_thread() called')

    def music_delivery(self, session, frames, frame_size, num_frames,
            sample_type, sample_rate, channels):
        """Callback used by pyspotify"""
        # pylint: disable = R0913
        # Too many arguments (8/5)
        assert sample_type == 0, u'Expects 16-bit signed integer samples'
        capabilites = """
            audio/x-raw-int,
            endianness=(int)1234,
            channels=(int)%(channels)d,
            width=(int)16,
            depth=(int)16,
            signed=(boolean)true,
            rate=(int)%(sample_rate)d
        """ % {
            'sample_rate': sample_rate,
            'channels': channels,
        }
        self.output.deliver_data(capabilites, bytes(frames))

    def play_token_lost(self, session):
        """Callback used by pyspotify"""
        logger.debug(u'Play token lost')
        # TODO-PYKKA: Send 'stop_playback' to backend

    def log_message(self, session, data):
        """Callback used by pyspotify"""
        logger.debug(u'System message: %s' % data.strip())

    def end_of_track(self, session):
        """Callback used by pyspotify"""
        logger.debug(u'End of data stream reached')
        self.output.end_of_data_stream()

    def refresh_stored_playlists(self):
        """Refresh the stored playlists in the backend with fresh meta data
        from Spotify"""
        playlists = []
        for spotify_playlist in self.session.playlist_container():
            playlists.append(
                SpotifyTranslator.to_mopidy_playlist(spotify_playlist))
        playlists = filter(None, playlists)
        # TODO-PYKKA: Set stored_playlists on backend
        logger.debug(u'Refreshed %d stored playlist(s)', len(playlists))

    def search(self, query, connection):
        """Search method used by Mopidy backend"""
        def callback(results, userdata=None):
            # TODO Include results from results.albums(), etc. too
            playlist = Playlist(tracks=[
                SpotifyTranslator.to_mopidy_track(t)
                for t in results.tracks()])
            connection.send(playlist)
        self.connected.wait()
        self.session.search(query, callback)

@@ -197,16 +197,57 @@
 ettings%0A
+from mopidy.backends.base import Backend%0A
 from mop
@@ -303,16 +303,16 @@
 nslator%0A
-
 from mop
@@ -1063,182 +1063,54 @@
     
-output_refs = ActorRegistry.get_by_class(BaseOutput)%0A        assert len(output_refs) == 1, 'Expected exactly one running output.'%0A        self.output = output_refs%5B0%5D.proxy()
+self.output = None%0A        self.backend = None
 %0A%0A  
@@ -1201,32 +1201,53 @@
 side_try(self):%0A
+        self.setup()%0A
         self.con
@@ -1250,24 +1250,415 @@
 .connect()%0A%0A
+    def setup(self):%0A        output_refs = ActorRegistry.get_by_class(BaseOutput)%0A        assert len(output_refs) == 1, 'Expected exactly one running output.'%0A        self.output = output_refs%5B0%5D.proxy()%0A%0A        backend_refs = ActorRegistry.get_by_class(Backend)%0A        assert len(backend_refs) == 1, 'Expected exactly one running backend.'%0A        self.backend = backend_refs%5B0%5D.proxy()%0A%0A
     def logg
@@ -3700,53 +3700,37 @@
     
-# TODO-PYKKA: Send 'stop_playback' to backend
+self.backend.playback.pause()
 %0A%0A  
@@ -4430,26 +4430,21 @@
     
-# TODO-PYKKA: Set 
+self.backend.
 stor
@@ -4460,18 +4460,19 @@
 sts 
-on backend
+= playlists
 %0A   

Reimplement pause on lost play token and setting of stored playlists