# -*- coding: utf-8 -*-
# Elisa - Home multimedia server
# Copyright (C) 2006-2008 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 Elisa with Fluendo's plugins.
#
# The GPL part of Elisa is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Elisa" in the root directory of this distribution package
# for details on that license.


__maintainer__ = 'Benjamin Kampmann <benjamin@fluendo.com>'


from elisa.base_components.media_provider import MediaProvider
from elisa.core.media_uri import quote, MediaUri
from elisa.core import common

from elisa.extern.translation import gettexter, N_
T_ = gettexter('elisa-audiocd')

import pygst
pygst.require('0.10')
import gst

try:
    from  musicbrainz2 import disc, webservice
except ImportError:
    disc = None

import os, re

from twisted.internet import defer, threads

plugin_registry = common.application.plugin_registry
MediaLocationMessage = plugin_registry.get_component_class('base:media_location_message')

class NoCDFoundError(Exception):
    pass

class AudiocdMedia(MediaProvider):
    """
    This class implements the AudioCD support
    """
    name = "audiocd"

    def __init__(self):
        MediaProvider.__init__(self)
        self._discid = ""
        self._tracks = {}

    def _bus_message_received(self, msg, sender):
        if msg.fstype == 'cdda':
            if msg.action == MediaLocationMessage.ActionType.LOCATION_ADDED:
                self.debug("AudioCD found at %s" % msg.name)
            elif msg.action == MediaLocationMessage.ActionType.LOCATION_REMOVED:
                self.debug("AudioCD removed")
                self._discid = ""
                self._tracks = {}

    def initialize(self):
        common.application.bus.register(self._bus_message_received,
                                        MediaLocationMessage)

    def scannable_uri_schemes__get(self):
        return []

    def supported_uri_schemes__get(self):
        return {'cdda':0}

    def _blocking_get_media_type(self, uri):
        file_type = 'audio'
        mime_type = ''
        if self._blocking_is_directory(uri):
            file_type = 'directory'

        return {'file_type': file_type, 'mime_type': mime_type}

    def _blocking_is_directory(self, uri):
        return uri.host == '' and uri.parent.path == '/'

    def _blocking_has_children_with_types(self, uri, media_types):
        has_children = False
        if self._blocking_is_directory(uri):
            has_children = len(set(['audio',
                                    'directory']).intersection(media_types)) > 0
        return has_children
    
    def get_direct_children(self, uri, l):

        if not self._blocking_is_directory(uri):
            return defer.succeed(l)

        if disc:
            return threads.deferToThread(self._try_with_imdb, l)
        else:
            self.warning("Could not lookup the CD online. If you want to"
                         " you have to install the python musicbrainz"
                         " library. Doing a local lookup instead")
            return self._local_lookup(l)

    def _append_existing_tracks(self, list_of_children):
        # BAD! because it is blocking
        for track, album, artist in self._tracks.itervalues():
            list_of_children.append((track,
                                {'album' : album, 'artist': artist}))


    def _try_with_imdb(self, list_of_children):
        try:
            my_disc = disc.readDisc()
        except disc.DiscError, e:
            print "jojojojojo"
            return defer.succeed(list_of_children)

        if self._discid == my_disc.id:
            self._append_existing_tracks(list_of_children)
            return list_of_children

        self._discid = my_disc.id # set the discid
        self.debug("the disc-id is: %s " % my_disc.id)

        # refresh the tracks-cache
        self._tracks = {} 
        try:
            service = webservice.WebService()
            query = webservice.Query(service)
            filter = webservice.ReleaseFilter(discId=my_disc.getId())
            results = query.getReleases(filter)
            if len(results) == 0:
                raise NoCDFoundError()

            selectedRelease = results[0].release
            inc = webservice.ReleaseIncludes(artist=True,
                                             tracks=True,
                                             releaseEvents=True)
            release = query.getReleaseById(selectedRelease.getId(), inc)
            album = release.title
            artist = release.artist.getUniqueName()
            self.info("Found CD in Musicbrainz: %s - %s" %
                                            (artist, album))
            isSingleArtist = release.isSingleArtistRelease()

            i = 1
            for track in release.tracks:
                if not isSingleArtist:
                    artist = track.artist.name

                title = artist + ' - ' +  track.title
                self.debug( "Processing the Track: %s" % track)
                (minutes, seconds) = track.getDurationSplit()
                i+=1
                child_uri = MediaUri("cdda://%2d" % i)
                child_uri.label = "%s (%d:%02d)" % (title, minutes,
                                                            seconds)
                self._tracks[i] = (child_uri,album,artist)

        except (webservice.WebServiceError, NoCDFoundError):
            # FIXME: raise exception ?
            self.warning("Could not look up the CD, reading it without "
                         "meta informations")
            index = 1
            for (offset, length) in my_disc.tracks:
                child_uri = MediaUri("cdda://%s" % index)
                child_uri.label = T_(N_("Track %d"), index)
                self._tracks[index] = (child_uri,None,None)
                index += 1

        self._append_existing_tracks(list_of_children)

        return list_of_children

    def _local_lookup(self, list):
        pipe = gst.element_factory_make('playbin')
        pipe.set_property('uri', 'cdda://')
        dfr = defer.Deferred()

        def got_tags(bus, mes, list, dfr):
            if mes.type == gst.MESSAGE_TAG:
                pipe.set_state(gst.STATE_NULL)
                tl = mes.parse_tag()
                if 'track-count' in tl.keys():
                    tracks = int(tl['track-count'])
                    if tracks > 0:
                        i = 1
                        while i <= tracks:
                            uri = MediaUri('cdda://%s' % i)
                            uri.label = T_(N_("Track %s"), i)
                            list.append((uri, {}))
                            self._tracks[i] = (uri, None, None)
                            i += 1
                    dfr.callback(list)

        # connect to bus message
        pipe.get_bus().connect("message", got_tags, list, dfr)
        pipe.get_bus().add_signal_watch()

        pipe.set_state(gst.STATE_PAUSED)
        return dfr

    def next_location(self, uri, root=None):
        return threads.deferToThread(self._blocking_next_location, uri)

    def _blocking_next_location(self, uri):
        next_uri = None

        if self._blocking_is_directory(uri):
            next_track = 1
        else:
            track = int(uri.host)
            next_track = track +1


        if self._tracks.has_key(next_track):
            next_uri = self._tracks[next_track][0]

        return next_uri

    def previous_location(self, uri):
        return threads.deferToThread(self._blocking_previous_location, uri)

    def _blocking_previous_location(self, uri):
        if self._is_directory(uri):
            return None

        prev_uri = None
        track = int(uri.host)
        prev_track = track - 1

        if self._tracks.has_key(prev_track):
            prev_uri = self._tracks[prev_track][0]
        return prev_uri


    def uri_is_monitorable(self, uri):
        return False

    def uri_is_monitored(self, uri):
        return False


if __name__ == "__main__":

    m = AudiocdMedia()

    uri = MediaUri(u'cdda://')
    print "is it a dir? %s" % m._is_directory(uri)
    print "so what type is it? %s" % m.get_media_type(uri)
    uri = MediaUri(u'cdda://1?label=Track2')
    print "path is: %s. and host is %s" % (uri.path,uri.host)
    print "is it a dir? %s" % m._is_directory(uri)
    print "so what type is it? %s" % m.get_media_type(uri)
    uri = MediaUri(u'cdda:///dev/hda/Trackly')
    print "is it a dir? %s" % m._is_directory(uri)
    print "so what type is it? %s" % m.get_media_type(uri)
    uri = MediaUri(u'cdda:///dev/hda/0?label=Track2')
    print "is it a dir? %s" % m._is_directory(uri)
    print "so what type is it? %s" % m.get_media_type(uri)
