from django.conf import settings
from django.core.files.images import ImageFile
from django.utils.functional import LazyObject
from ..files import BaseIKFile
from ..registry import generator_registry
from ..signals import before_access
from ..utils import get_logger, get_singleton, generate, get_by_qname


class ImageCacheFile(BaseIKFile, ImageFile):
    """
    A file that represents the result of a generator. Creating an instance of
    this class is not enough to trigger the generation of the file. In fact,
    one of the main points of this class is to allow the creation of the file
    to be deferred until the time that the cache file strategy requires it.

    """
    def __init__(self, generator, name=None, storage=None, cachefile_backend=None):
        """
        :param generator: The object responsible for generating a new image.
        :param name: The filename
        :param storage: A Django storage object that will be used to save the
            file.
        :param cachefile_backend: The object responsible for managing the
            state of the file.

        """
        self.generator = generator

        name = name or getattr(generator, 'cachefile_name', None)
        if not name:
            fn = get_by_qname(settings.IMAGEKIT_CACHEFILE_NAMER, 'namer')
            name = fn(generator)
        self.name = name

        storage = storage or getattr(generator, 'cachefile_storage',
            None) or get_singleton(settings.IMAGEKIT_DEFAULT_FILE_STORAGE,
            'file storage backend')
        self.cachefile_backend = cachefile_backend or getattr(generator,
            'cachefile_backend', None)

        super(ImageCacheFile, self).__init__(storage=storage)

    def _require_file(self):
        before_access.send(sender=self, file=self)
        return super(ImageCacheFile, self)._require_file()

    def generate(self, force=False):
        if force:
            self._generate()
        else:
            self.cachefile_backend.ensure_exists(self)

    def _generate(self):
        # Generate the file
        content = generate(self.generator)

        actual_name = self.storage.save(self.name, content)

        if actual_name != self.name:
            get_logger().warning('The storage backend %s did not save the file'
                    ' with the requested name ("%s") and instead used'
                    ' "%s". This may be because a file already existed with'
                    ' the requested name. If so, you may have meant to call'
                    ' ensure_exists() instead of generate(), or there may be a'
                    ' race condition in the file backend %s. The saved file'
                    ' will not be used.' % (self.storage,
                    self.name, actual_name,
                    self.cachefile_backend))


class LazyImageCacheFile(LazyObject):
    def __init__(self, generator_id, *args, **kwargs):
        super(LazyImageCacheFile, self).__init__()

        def setup():
            generator = generator_registry.get(generator_id, *args, **kwargs)
            self._wrapped = ImageCacheFile(generator)

        self.__dict__['_setup'] = setup

    def __repr__(self):
        if self._wrapped is None:
            self._setup()
        return '<%s: %s>' % (self.__class__.__name__, self or 'None')

    def __str__(self):
        if self._wrapped is None:
            self._setup()
        return str(self._wrapped)

    def __unicode__(self):
        if self._wrapped is None:
            self._setup()
        return unicode(self._wrapped)
