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

# an internet radio media server for the Coherence UPnP Framework
# based on the radiotime (http://radiotime.com) catalog service

# Copyright 2007, Frank Scholz <coherence@beebits.net>
# Copyright 2009-2010, Jean-Michel Sizun <jmDOTsizunATfreeDOTfr>
from lxml import etree

from twisted.python.failure import Failure

from coherence.upnp.core import utils
from coherence.upnp.core import DIDLLite
from coherence.upnp.core.DIDLLite import Resource
from coherence.backend import BackendItem, Container, LazyContainer, AbstractBackendStore

OPML_BROWSE_URL = 'http://opml.radiotime.com/Browse.ashx'

# we only handle mp3 audio streams for now
DEFAULT_FORMAT = "mp3"
DEFAULT_MIMETYPE = "audio/mpeg"

# TODO : extend format handling using radiotime API

class RadiotimeAudioItem(BackendItem):
    logCategory = 'radiotime'

    def __init__(self, outline):
        BackendItem.__init__(self)
        self.preset_id = outline.get('preset_id')
        self.name = outline.get('text')
        self.mimetype = DEFAULT_MIMETYPE
        self.stream_url = outline.get('URL')
        self.image = outline.get('image')
        #self.location = PlaylistStreamProxy(self.stream_url)
        #self.url = self.stream_url

        self.item = None

    def replace_by (self, item):
        # do nothing: we suppose the replacement item is the same
        return

    def get_item(self):
        if self.item == None:
            upnp_id = self.get_id()
            upnp_parent_id = self.parent.get_id()
            self.item = DIDLLite.AudioBroadcast(upnp_id, upnp_parent_id, self.name)
            self.item.albumArtURI = self.image
            res = Resource(self.stream_url, 'http-get:*:%s:%s' % (self.mimetype,
                                                           ';'.join(('DLNA.ORG_PN=MP3',
                                                                     'DLNA.ORG_CI=0',
                                                                     'DLNA.ORG_OP=01',
                                                                     'DLNA.ORG_FLAGS=01700000000000000000000000000000'))))
            res.size = 0  # None
            self.item.res.append(res)
        return self.item

    def get_path(self):
        return self.url

    def get_id(self):
        return self.storage_id

    
class RadiotimeStore(AbstractBackendStore):

    logCategory = 'radiotime'

    implements = ['MediaServer']

    def __init__(self, server, **kwargs):
        AbstractBackendStore.__init__(self, server, **kwargs)

        self.name = kwargs.get('name', 'radiotimeStore')
        self.refresh = int(kwargs.get('refresh', 60)) * 60

        self.browse_url = self.config.get('browse_url', OPML_BROWSE_URL)
        self.partner_id = self.config.get('partner_id', 'TMe3Cn6v')
        self.username = self.config.get('username', None)
        self.locale = self.config.get('locale', 'en')
        self.serial = server.uuid

        # construct URL for root menu
        if self.username is not None:
            identification_param = "username=%s" % self.username
        else:
            identification_param = "serial=%s" % self.serial
        formats_value = DEFAULT_FORMAT
        root_url = "%s?partnerId=%s&%s&formats=%s&locale=%s" % (self.browse_url, self.partner_id, identification_param, formats_value, self.locale)

        # set root item
        root_item = LazyContainer(None, "root", "root", self.refresh, self.retrieveItemsForOPML, url=root_url)
        self.set_root_item(root_item)

        self.init_completed()

    def upnp_init(self):
        self.current_connection_id = None

        self.wmc_mapping = {'4': self.get_root_id()}

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

    def retrieveItemsForOPML (self, parent, url):

        def append_outline(parent, outline):
            type = outline.get('type')
            if type is None:
                # This outline is just a classification item containing other outline elements
                # the corresponding item will a static Container
                text = outline.get('text')
                key = outline.get('key')
                external_id = None
                if external_id is None and key is not None:
                    external_id = "%s_%s" % (parent.external_id, key)
                if external_id is None:
                    external_id = outline_url
                item = Container(parent, text)
                item.external_id = external_id
                item.store = parent.store
                parent.add_child(item, external_id=external_id)
                sub_outlines = outline.findall('outline')
                for sub_outline in sub_outlines:
                    append_outline(item, sub_outline)

            elif type == 'link':
                # the corresponding item will a self-populating Container
                text = outline.get('text')
                outline_url = outline.get('URL')
                key = outline.get('key')
                guide_id = outline.get('guide_id')
                external_id = guide_id
                if external_id is None and key is not None:
                    external_id = "%s_%s" % (parent.external_id, key)
                if external_id is None:
                    external_id = outline_url
                item = LazyContainer(parent, text, external_id, self.refresh, self.retrieveItemsForOPML, url=outline_url)
                parent.add_child(item, external_id=external_id)

            elif type == 'audio':
                item = RadiotimeAudioItem(outline)
                parent.add_child(item, external_id=item.preset_id)

        def got_page(result):
            self.info('connection to Radiotime service successful for url %s', url)

            outlines = result.findall('body/outline')
            for outline in outlines:
                append_outline(parent, outline)

            return True

        def got_error(error):
            self.warning("connection to Radiotime service failed for url %s", url)
            self.debug("%r", error.getTraceback())
            parent.childrenRetrievingNeeded = True  # we retry
            return Failure("Unable to retrieve items for url %s" % url)

        def got_xml_error(error):
            self.warning("Data received from Radiotime service is invalid: %s", url)
            #self.debug("%r", error.getTraceback())
            print error.getTraceback()
            parent.childrenRetrievingNeeded = True  # we retry
            return Failure("Unable to retrieve items for url %s" % url)

        d = utils.getPage(url, )
        d.addCallback(etree.fromstring)
        d.addErrback(got_error)
        d.addCallback(got_page)
        d.addErrback(got_xml_error)
        return d
