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

import pgm
from drawable import Drawable

from elisa.core.utils import defer

class Image(Drawable, pgm.Image):
    """
    Image node of the scenegraph inheriting from pgm.Image. It is a Pigment
    image drawable with a few extra characteristics:
     - defines a 'file' property allowing to load an image file with a variable
       assignment

    @ivar file: path of the image file to load
                it is a simple proxy to pgm.Image.set_from_file
    @type file: str
    @cvar canvas: reference to the canvas used for the caching mechanism
    @type canvas: L{pgm.Canvas}
    """

    canvas = None
    # Store master images used for the caching mechanism
    # Key:    path to image files (str)
    # Values: master image drawables (elisa.plugins.pigment.graph.image.Image)
    _master_drawables = {}

    def __init__(self):
        pgm.Image.__init__(self)
        Drawable.__init__(self)

    def clean(self):
        self.clear()
        return super(Image, self).clean()

    def _set_file(self, file):
        self.set_from_file_with_caching(file)

    file = property(fset=_set_file)

    def set_from_file_with_caching(self, file_path):
        """
        Load an image file at L{file_path} into the image drawable using a
        caching mechanism: a given file is loaded only once in texture memory
        and the texture is reused for all subsequent calls to this method.

        Return a cancellable deferred fired when the image file is loaded.

        @param file_path: image file to load
        @type file_path: C{str}

        @rtype: L{elisa.core.utils.cancellable_defer.CancellableDeferred}
        """
        try:
            # reuse an existent master drawable
            master = Image._master_drawables[file_path]
        except KeyError:
            # create a master drawable, insert it in the canvas and store it
            master = Image()
            Image._master_drawables[file_path] = master
            Image.canvas.add(pgm.DRAWABLE_FAR, master)

            # load the requested image file at file_path
            master.dfr = master.set_from_file_deferred(file_path)

        # clone the master drawable
        self.set_from_image(master)

        # manually emit the 'file-loaded' signal when it is finished loading
        # into the master drawable
        def emit_image_loaded(result):
            self.emit("file-loaded")
        master.dfr.addCallback(emit_image_loaded)

        return master.dfr

    @classmethod
    def new_from_file_deferred(cls, file_path, max_size=0):
        """
        Wrapper for pgm.Image.new_from_file. Acts the same but return a
        cancellable deferred fired when the image file is loaded that will
        contain the instance as a result.

        @param file_path: image file to load
        @type  file_path: str
        @param max_size:  size in pixels to downscale the picture to if needed;
                          0 for no downscale
        @type max_size:   int
        @rtype: L{elisa.core.utils.cancellable_defer.CancellableDeferred}
        """
        image = cls()
        return image.set_from_file_deferred(file_path, max_size)

    def set_from_file_deferred(self, file_path, max_size=0):
        """
        Wrapper for pgm.Image.set_from_file. Acts the same but return a
        cancellable deferred fired when the image file is loaded.

        @param file_path: image file to load
        @type  file_path: str
        @param max_size:  size in pixels to downscale the picture to if needed;
                          0 for no downscale
        @type max_size:   int
        @rtype: L{elisa.core.utils.cancellable_defer.CancellableDeferred}
        """
        def cancel_file_loading(dfr):
            self.clear()

        # this deferred will be fired when the file is finished loading that
        # is when the 'file-loaded' signal is emitted
        dfr = defer.Deferred(canceller=cancel_file_loading)

        def file_loaded_callback(self):
            dfr.callback(self)

        id = self.connect('file-loaded', file_loaded_callback)

        def disconnect(result, id):
            self.disconnect(id)
            return result

        # disconnect from the signal when the loading is not happening
        # anylonger
        dfr.addBoth(disconnect, id)

        # finally, do the actual request to load the file
        self.set_from_file(file_path, max_size)

        return dfr
