# -*- 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.

"""
Provides access to resources
"""

__maintainer__ = 'Florian Boucault <florian@fluendo.com>'
__maintainer2__ = 'Benjamin Kampmann <benjamin@fluendo.com>'


from elisa.core.component import Component

from elisa.core.utils import defer

class ResourceNotFound(Exception):
    """
    Exception raised by a resource provider if a requested resource cannot
    be found. 

    For instance, that is what should be raised when an HTTP 404 response code
    is obtained while trying to get the resource.
    """


class ResourceProvider(Component):
    """
    Responsible for accessing resources following REST architecture
    principles.

    Accessing a resource is done through a URI that points to it. Possible
    operations on resources are defined by the REST principles and allow
    retrieval, creation, update and removal of resources.

    For more information about REST, Roy Fielding's thesis is helpful:
    http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm

    A resource can be identified with a URI. This is also called a pointer
    or a link.


    Example:

    You could have a list of music albums stored at some place designated by
    the following URI::

        elisa://localhost/albums

    When the user decides to access the resource behind this URI, a retrieval
    request is sent to the L{elisa.resource_manager.ResourceManager}.
    It holds all the ResourceProviders and forwards the request to one that
    can handle it. This is made possible by trying to match the URI to their
    regular expressions.

    A resource in Elisa is stored as a L{elisa.core.components.model.Model}.
    The chosen ResourceProvider returns the Model (in this example, it would
    be something like an AlbumsListModel) and starts to fill it asynchronously.
    When it is complete, the L{elisa.core.utils.defer.Deferred} is fired.

    @cvar supported_uri: regular expression defining what URIs a
                         ResourceProvider can handle
    @type supported_uri: str
    """

    def get(self, uri, context_model=None):
        """
        Return a resource that L{uri} is pointing to. A URI can point to
        any kind of resource. Resources are returned as models.

        The model that is returned does not always already contain all the
        resource. The deferred is fired when the resource loading is complete.

        @param uri:           URI pointing to the resource
        @type uri:            L{elisa.core.media_uri.MediaUri}
        @param context_model: the URI often comes from a certain context.
                              For example a URI pointing to a MusicAlbum can
                              come from a Model that could contain the album
                              cover or the album name. If the context_model is
                              provided the resource_provider should try to
                              reuse its data if possible.
        @type context_model:  L{elisa.core.components.model.Model}

        @return:              a new model and a deferred fired when the
                              model is fully loaded
        @rtype:               tuple of L{elisa.core.components.model.Model}
                              L{elisa.core.utils.defer.Deferred}
        """
        return (None, defer.fail(NotImplementedError()))

    def post(self, uri, **parameters):
        """
        Update the resource pointed by L{uri} with L{parameters}.

        @param uri:        URI pointing to the resource to update
        @type uri:         L{elisa.core.media_uri.MediaUri}
        @param parameters: parameters of the resource that should be updated

        @return:           a deferred fired when the parameters got posted
        @rtype:            L{elisa.core.utils.defer.Deferred}
        """
        return defer.fail(NotImplementedError())

    def put(self, source_uri, container_uri, source_model=None):
        """
        Put one resource into another. Both resources are identified with URIs.

        @param source_uri:    URI pointing to the resource that should be put
                              into the other one
        @type source_uri:     L{elisa.core.media_uri.MediaUri}
        @param container_uri: URI pointing to the resource that should receive
                              the resource
        @type container_uri:  L{elisa.core.media_uri.MediaUri}
        @param source_model:  Often the resource behind the source_uri is
                              already existing as a model-representation. To
                              prevent from doing a 'get' for the given
                              source_uri this model can also be given to the 
                              put request.
        @type source_model:   L{elisa.core.components.model.Model}

        @return:              a deferred fired when the resource got put
        @rtype:               L{elisa.core.utils.defer.Deferred}
        """
        return defer.fail(NotImplementedError())

    def delete(self, uri):
        """
        Delete a Resource represented by a URI.

        @param uri: URI pointing to the resource that should be deleted
        @type uri:  L{elisa.media_uri.MediaUri}

        @return:    a deferred fired when the resource got deleted
        @rtype:     L{elisa.core.utils.defer.Deferred}
        """
        return defer.fail(NotImplementedError())
