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

"""
A resource provider for discogs.com.
Allows retrieval of artists' information and pictures.
"""

from elisa.core.components.resource_provider import ResourceProvider
from elisa.core.media_uri import MediaUri
from elisa.core.utils import defer

from elisa.plugins.discogs.discogs_api import SERVER

from elisa.plugins.http_client.http_client import ElisaAdvancedHttpClient, \
                                                  ElisaHttpClientNotOpenedError
from elisa.plugins.http_client.extern.client_http import ClientRequest

# models
from elisa.plugins.base.models.audio import ArtistModel
from elisa.plugins.base.models.image import ImageModel
from elisa.plugins.base.models.media import RawDataModel

from elisa.extern.coherence.et import parse_xml

from twisted.web2 import responsecode
from twisted.web2.stream import BufferedStream

import gzip, cStringIO


class DiscogsResource(ResourceProvider):

    """
    A resource provider for discogs.com.
    Currently supports query to the API for artists, and query for images.
    """

    supported_uri = '^%s' % SERVER

    def __init__(self):
        super(DiscogsResource, self).__init__()
        self._client = ElisaAdvancedHttpClient('www.discogs.com', 80)

    def get(self, uri, context_model=None):
        if context_model is not None:
            model = context_model
        else:
            if uri.path.startswith("/image"):
                model = RawDataModel()
            else:
                model = ArtistModel()

        # The discogs server doesn't like requests with an absolute URI
        # (it responds 404), strip the scheme and host:
        url = str(uri)[len(SERVER):]

        def got_response(response, model):
            if response.code == responsecode.OK:
                # Read the response stream
                dfr = BufferedStream(response.stream).readExactly()

                if isinstance(model, RawDataModel):
                    dfr.addCallback(self._write_raw_data, model)
                    return dfr

                # If the response is gzip-encoded, unzip it before parsing
                encoding = response.headers.getRawHeaders('Content-Encoding')
                if encoding is not None and encoding[0] == 'gzip':
                    dfr.addCallback(self._unzip)

                # Parse the response
                dfr.addCallback(self._parse_xml_response, model)
                return dfr

            elif response.code == responsecode.NOT_FOUND:
                # 404 error code: resource not found
                return defer.fail(IOError('Resource not found at %s' % url))

            else:
                # Other HTTP response code
                msg = 'Received an %d HTTP response code' % response.code
                return defer.fail(Exception(msg))

        # Custom request to accept gzip-encoded responses.
        # According to the API documentation, "the service will gzip most API
        # responses so your application must be able to decode gzipped data".
        request = ClientRequest('GET', url, None, None)
        request.headers.setRawHeaders('Accept-Encoding', ['gzip'])

        dfr = self._client.request_full(request)
        dfr.addCallback(got_response, model)
        return model, dfr

    def clean(self):
        def eater(failure):
            failure.trap(ElisaHttpClientNotOpenedError)

        dfr = self._client.close()
        dfr.addErrback(eater)

        def parent_clean(result):
            return super(DiscogsResource, self).clean()

        dfr.addCallback(parent_clean)
        return dfr

    def _write_raw_data(self, data, model):
        # Write raw (image) data to a RawDataModel.
        model.data = data
        model.size = len(data)
        return model

    def _unzip(self, compressed_data):
        # Unzip gzip-compressed data and return the uncompressed raw data.
        fileobj = cStringIO.StringIO(compressed_data)
        return gzip.GzipFile(fileobj=fileobj).read()

    def _parse_xml_response(self, data, model):
        # Parse an XML API response into the corresponding model.
        # At the moment only the artist name and the artist pictures, with
        # thumbnails, are used from the xml data.
        et = parse_xml(data)
        if et:
            # get artist name
            artist = et.findtext(".//name")
            # clear the image list if request was made for a new artist
            if model.name != artist:
                del model.images[:]
                model.name = artist
            for images in et.getiterator("image"):
                d = ImageModel()
                d.references = [MediaUri(images.get("uri")),
                                MediaUri(images.get("uri150"))]
                model.images.append(d)

        return model
