# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php

"""
INFO  lastFM_user                 Dez 14 17:35:27  Got new sessionid: '1488f34a1cbed7c9f4232f8fd563c3bd' (coherence/backends/lastfm_storage.py:60)
DEBUG lastFM_stream               Dez 14 17:35:53  render <GET /da525474-5357-4d1b-a894-76b1293224c9/1005 HTTP/1.1> (coherence/backends/lastfm_storage.py:148)
command GET
rest /user/e0362c757ef49169e9a0f0970cc2d367.mp3
headers {'icy-metadata': '1', 'host': 'kingpin5.last.fm', 'te': 'trailers', 'connection': 'TE', 'user-agent': 'gnome-vfs/2.12.0.19 neon/0.24.7'}
ProxyClient handleStatus HTTP/1.1 200 OK
ProxyClient handleHeader Content-Type audio/mpeg
ProxyClient handleHeader Content-Length 4050441
ProxyClient handleHeader Cache-Control no-cache, must-revalidate
DEBUG lastFM_stream               Dez 14 17:35:53  render <GET /da525474-5357-4d1b-a894-76b1293224c9/1005 HTTP/1.1> (coherence/backends/lastfm_storage.py:148)
command GET
rest /user/e0362c757ef49169e9a0f0970cc2d367.mp3
headers {'icy-metadata': '1', 'host': 'kingpin5.last.fm', 'te': 'trailers', 'connection': 'TE', 'user-agent': 'gnome-vfs/2.12.0.19 neon/0.24.7'}
ProxyClient handleStatus HTTP/1.1 403 Invalid ticket
"""
# Copyright 2007, Frank Scholz <coherence@beebits.net>
# Copyright 2007, Moritz Struebe <morty@gmx.net>

from twisted.internet import defer

from coherence.upnp.core import utils

from coherence.upnp.core.DIDLLite import classChooser, Container, Resource, DIDLElement

import coherence.extern.louie as louie

from coherence.extern.simple_plugin import Plugin

from coherence import log
from coherence.backend import BackendItem, BackendStore

from urlparse import urlsplit

try:
    from hashlib import md5
except ImportError:
    # hashlib is new in Python 2.5
    from md5 import md5

import string


class LastFMUser(log.Loggable):

    logCategory = 'lastFM_user'

    user = None
    passwd = None
    host = "ws.audioscrobbler.com"
    basepath = "/radio"
    sessionid = None
    parent = None
    getting_tracks = False
    tracks = []

    def __init__(self, user, passwd):
        log.Loggable.__init__(self)
        if user is None:
            self.warn("No User", )
        if passwd is None:
            self.warn("No Passwd", )
        self.user = user
        self.passwd = passwd

    def login(self):

        if self.sessionid != None:
            self.warning("Session seems to be valid", )
            return

        def got_page(result):
            lines = result[0].split("\n")
            for line in lines:
                tuple = line.rstrip().split("=", 1)
                if len(tuple) == 2:
                    if tuple[0] == "session":
                        self.sessionid = tuple[1]
                        self.info("Got new sessionid: %r", self.sessionid)
                    if tuple[0] == "base_url":
                        if(self.host != tuple[1]):
                            self.host = tuple[1]
                            self.info("Got new host: %s", self.host)
                    if tuple[0] == "base_path":
                        if(self.basepath != tuple[1]):
                            self.basepath = tuple[1]
                            self.info("Got new path: %s", self.basepath)
            self.get_tracks()

        def got_error(error):
            self.warning("Login to LastFM Failed! %r", error)
            self.debug("%r", error.getTraceback())

        def hexify(s):  # This function might be GPL! Found this code in some other Projects, too.
            result = ""
            for c in s:
                result = result + ("%02x" % ord(c))
            return result
        password = hexify(md5(self.passwd).digest())
        req = self.basepath + "/handshake.php/?version=1&platform=win&username=" + self.user + "&passwordmd5=" + password + "&language=en&player=coherence"
        utils.getPage("http://" + self.host + req).addCallbacks(got_page, got_error, None, None, None, None)

    def get_tracks(self):
        if self.getting_tracks == True:
            return

        def got_page(result):
            result = utils.parse_xml(result, encoding='utf-8')
            self.getting_tracks = False
            print self.getting_tracks
            print "got Tracks"
            for track in result.findall('trackList/track'):
                data = {}

                def get_data(name):
                    #print track.find(name).text.encode('utf-8')
                    return track.find(name).text.encode('utf-8')
                #Fixme: This section needs some work
                print "adding Track"
                data['mimetype'] = 'audio/mpeg'
                data['name'] = get_data('creator') + " - " + get_data('title')
                data['title'] = get_data('title')
                data['artist'] = get_data('creator')
                data['creator'] = get_data('creator')
                data['album'] = get_data('album')
                data['duration'] = get_data('duration')
                #FIXME: Image is the wrong tag.
                data['image'] = get_data('image')
                data['url'] = track.find('location').text.encode('utf-8')
                item = self.parent.store.append(data, self.parent)
                self.tracks.append(item)

        def got_error(error):
            self.warning("Problem getting Tracks! %r", error)
            self.debug("%r", error.getTraceback())
            self.getting_tracks = False

        self.getting_tracks = True
        req = self.basepath + "/xspf.php?sk=" + self.sessionid + "&discovery=0&desktop=1.3.1.1"
        utils.getPage("http://" + self.host + req).addCallbacks(got_page, got_error, None, None, None, None)

    def update(self, item):
        if 0 < self.tracks.count(item):
            while True:
                track = self.tracks[0]
                if track == item:
                    break
                self.tracks.remove(track)
                # Do not remoce so the tracks to answer the browse
                # request correctly.
                #track.store.remove(track)
                #del track

        #if len(self.tracks) < 5:
        self.get_tracks()


class LFMProxyStream(utils.ReverseProxyResource, log.Loggable):
    logCategory = 'lastFM_stream'

    def __init__(self, uri, parent):
        log.Loggable.__init__(self)
        self.uri = uri
        self.parent = parent
        _, host_port, path, _, _ = urlsplit(uri)
        if host_port.find(':') != -1:
            host, port = tuple(host_port.split(':'))
            port = int(port)
        else:
            host = host_port
            port = 80

        if path == '':
            path = '/'

        #print "ProxyStream init", host, port, path
        utils.ReverseProxyResource.__init__(self, host, port, path)

    def render(self, request):
        self.debug("render %r", request)
        self.parent.store.LFM.update(self.parent)
        self.parent.played = True
        return utils.ReverseProxyResource.render(self, request)


class LastFMItem(log.Loggable):
    logCategory = 'LastFM_item'

    def __init__(self, id, obj, parent, mimetype, urlbase, UPnPClass, update=False):
        log.Loggable.__init__(self)
        self.id = id

        self.name = obj.get('name')
        self.title = obj.get('title')
        self.artist = obj.get('artist')
        self.creator = obj.get('creator')
        self.album = obj.get('album')
        self.duration = obj.get('duration')
        self.mimetype = mimetype

        self.parent = parent
        if parent:
            parent.add_child(self, update=update)

        if parent == None:
            parent_id = -1
        else:
            parent_id = parent.get_id()

        self.item = UPnPClass(id, parent_id, self.title, False, self.creator)
        if isinstance(self.item, Container):
            self.item.childCount = 0
        self.child_count = 0
        self.children = []

        if(len(urlbase) and urlbase[-1] != '/'):
            urlbase += '/'

        if self.mimetype == 'directory':
            self.url = urlbase + str(self.id)
        else:
            self.url = urlbase + str(self.id)
            self.location = LFMProxyStream(obj.get('url'), self)
            #self.url = obj.get('url')

        if self.mimetype == 'directory':
            self.update_id = 0
        else:
            res = Resource(self.url, 'http-get:*:%s:%s' % (obj.get('mimetype'),
                                                                     ';'.join(('DLNA.ORG_PN=MP3',
                                                                               'DLNA.ORG_CI=0',
                                                                               'DLNA.ORG_OP=01',
                                                                               'DLNA.ORG_FLAGS=01700000000000000000000000000000'))))
            res.size = -1  # None
            self.item.res.append(res)

    def remove(self):
        if self.parent:
            self.parent.remove_child(self)
        del self.item

    def add_child(self, child, update=False):
        if self.children == None:
            self.children = []
        self.children.append(child)
        self.child_count += 1
        if isinstance(self.item, Container):
            self.item.childCount += 1
        if update == True:
            self.update_id += 1

    def remove_child(self, child):
        self.info("remove_from %d (%s) child %d (%s)", self.id, self.get_name(), child.id, child.get_name())
        if child in self.children:
            self.child_count -= 1
            if isinstance(self.item, Container):
                self.item.childCount -= 1
            self.children.remove(child)
            self.update_id += 1

    def get_children(self, start=0, request_count=0):
        if request_count == 0:
            return self.children[start:]
        else:
            return self.children[start:request_count]

    def get_child_count(self):
        if self.mimetype == 'directory':
            return 100  # Some Testing, with strange Numbers: 0/lots
        return self.child_count

    def get_id(self):
        return self.id

    def get_update_id(self):
        if hasattr(self, 'update_id'):
            return self.update_id
        else:
            return None

    def get_path(self):
        return self.url

    def get_name(self):
        return self.name

    def get_parent(self):
        return self.parent

    def get_item(self):
        return self.item

    def get_xml(self):
        return self.item.toString()

    def __repr__(self):
        return 'id: ' + str(self.id) + ' @ ' + self.url + ' ' + self.name


class LastFMStore(log.Loggable, Plugin):

    logCategory = 'lastFM_store'

    implements = ['MediaServer']

    def __init__(self, server, **kwargs):
        log.Loggable.__init__(self)

        self.next_id = 1000
        self.config = kwargs
        self.name = kwargs.get('name', 'LastFMStore')

        self.update_id = 0
        self.store = {}

        self.wmc_mapping = {'4': 1000}

        louie.send('Coherence.UPnP.Backend.init_completed', None, backend=self)

    def __repr__(self):
        return str(self.__class__).split('.')[-1]

    def append(self, obj, parent):
        if isinstance(obj, basestring):
            mimetype = 'directory'
        else:
            mimetype = obj['mimetype']

        UPnPClass = classChooser(mimetype)
        id = self.getnextID()
        update = False
        if hasattr(self, 'update_id'):
            update = True

        self.store[id] = LastFMItem(id, obj, parent, mimetype, self.urlbase,
                                        UPnPClass, update=update)
        self.store[id].store = self


        if hasattr(self, 'update_id'):
            self.update_id += 1
            if self.server:
                self.server.content_directory_server.set_variable(0, 'SystemUpdateID', self.update_id)
            if parent:
                #value = '%d,%d' % (parent.get_id(),parent_get_update_id())
                value = (parent.get_id(), parent.get_update_id())
                if self.server:
                    self.server.content_directory_server.set_variable(0, 'ContainerUpdateIDs', value)

        return self.store[id]

    def remove(self, item):
        try:
            parent = item.get_parent()
            item.remove()
            del self.store[int(id)]
            if hasattr(self, 'update_id'):
                self.update_id += 1
                if self.server:
                    self.server.content_directory_server.set_variable(0, 'SystemUpdateID', self.update_id)
                #value = '%d,%d' % (parent.get_id(),parent_get_update_id())
                value = (parent.get_id(), parent.get_update_id())
                if self.server:
                    self.server.content_directory_server.set_variable(0, 'ContainerUpdateIDs', value)
        except:
            pass

    def len(self):
        return len(self.store)

    def get_by_id(self, id):
        if isinstance(id, basestring):
            id = id.split('@', 1)
            id = id[0]
        id = int(id)
        if id == 0:
            id = 1000
        try:
            return self.store[id]
        except:
            return None

    def getnextID(self):
        ret = self.next_id
        self.next_id += 1
        return ret

    def upnp_init(self):
        self.current_connection_id = None

        parent = self.append({'name': 'LastFM', 'mimetype': 'directory'}, None)

        self.LFM = LastFMUser(self.config.get("login"), self.config.get("password"))
        self.LFM.parent = parent
        self.LFM.login()

        if self.server:
            self.server.connection_manager_server.set_variable(0, 'SourceProtocolInfo',
                                                                    ['http-get:*:audio/mpeg:*'],
                                                                    default=True)


def main():

    f = LastFMStore(None)

    def got_upnp_result(result):
        print "upnp", result

    f.upnp_init()



if __name__ == '__main__':

    from twisted.internet import reactor

    reactor.callWhenRunning(main)
    reactor.run()
