from django.db.models.fields.files import ImageField, ImageFieldFile
from django.core.files.base import File, ContentFile

class ImageVariation(File):

    variation_suffix = 'variation'

    def __init__(self, instance):
        self.field = instance.field
        self.instance = instance.instance
        self.storage = getattr(self, 'storage', instance.storage)
        name = getattr(self.instance, self.field.name) or u''
        self._name = self.get_variation_name(unicode(name))
        self._closed = False

    def get_variation_name(self, name):
        try:
            dot_index = name.rindex('.')
        except:
            name = '%s%s' % (name, self.variation_suffix)
        else:
            name = '%s%s%s' % (name[:dot_index], self.variation_suffix, name[dot_index:])
        return name

    def save(self, name, content):
        self.storage.save(self.name, content)

    def delete(self):
        self.close()
        self.storage.delete(self.name)
    def _require_file(self):
        if not self:
            raise ValueError("The '%s' attribute has no file associated with it." % self.field.name)

    def _get_file(self):
        self._require_file()
        if not hasattr(self, '_file'):
            self._file = self.storage.open(self.name, 'rb')
        return self._file
    file = property(_get_file)

    def _get_path(self):
        self._require_file()
        return self.storage.path(self.name)
    path = property(_get_path)

    def _get_url(self):
        self._require_file()
        return self.storage.url(self.name)
    url = property(_get_url)

    def open(self, mode='rb'):
        self._require_file()
        return super(ImageVariation, self).open(mode)
    # open() doesn't alter the file's contents, but it does reset the pointer
    open.alters_data = True

from StringIO import StringIO
def get_file(data):
    if hasattr(data, 'temporary_file_path'):
         file = data.temporary_file_path()
    else:
        if hasattr(data, 'read'):
            file = StringIO(data.read())
        else:
            file = StringIO(data['content'])
    return file

WIDTH, HEIGHT = 0, 1
def resize_image(content, size):
    from PIL import Image
    img = Image.open(get_file(content))
    if img.size[0] > size['width'] or img.size[1] > size['height']:
        if size['force']:
            target_height = float(size['height'] * img.size[WIDTH]) / size['width']
            if target_height < img.size[HEIGHT]: # Crop height
                crop_side_size = int((img.size[HEIGHT] - target_height) / 2)
                img = img.crop((0, crop_side_size, img.size[WIDTH], img.size[HEIGHT] - crop_side_size))
            elif target_height > img.size[HEIGHT]: # Crop width
                target_width = float(size['width'] * img.size[HEIGHT]) / size['height']
                crop_side_size = int((img.size[WIDTH] - target_width) / 2)
                img = img.crop((crop_side_size, 0, img.size[WIDTH] - crop_side_size, img.size[HEIGHT]))
        img.thumbnail((size['width'], size['height']), Image.ANTIALIAS)
        out = StringIO()
        try:
            img.save(out, optimize=1)
        except IOError:
            img.save(out)
        return out
    else:
        return content

class Thumbnail(ImageVariation):
    variation_suffix = 'thumbnail'

    def save(self, name, content):
        self.storage.save(self.name, resize_image(content, {'height': 200, 'width': 200, 'force': True} ) )

class ImageVariationsDescriptor(object):

    def __get__(self, instance=None, owner=None):
        self.variations = [ (v(instance))
            for v in instance.field.variations ]
        for v in self.variations:
            setattr(self, v.__class__.__name__.lower(), v)
        return self

    def save(self, name, content):
        for v in self.variations:
            content.seek(0)
            v.save(name, content)

    def delete(self):
        for v in self.variations:
            v.delete()


class ImageVariationsFieldFile(ImageFieldFile):

    variations = ImageVariationsDescriptor()

    def save(self, name, content, save=True):
        super(ImageVariationsFieldFile, self).save(name, content, save=save)
        self.variations.save(name, content)

    def delete(self, save=True):
        self.variations.delete()
        super(ImageVariationsFieldFile, self).delete(save)

class ImageVariationsField(ImageField):
    attr_class = ImageVariationsFieldFile

    def __init__(self, variations=[], *args, **kwargs):
        self.variations = variations
        super(ImageVariationsField, self).__init__(*args, **kwargs)