import zipfile
from zipfile import BadZipFile
import logging
import datetime
import os
from io import BytesIO
try:
    import Image
except ImportError:
    from PIL import Image

from django import forms
from django.utils.translation import ugettext_lazy as _
from django.utils.safestring import mark_safe
from django.contrib import messages
from django.core.files.base import ContentFile
from django.contrib.admin import widgets
from django.conf import settings

from photos.models import LuxImage, LuxGallery, LuxImageSize

from .utils import resize_image
from .readexif import readexif

logger = logging.getLogger('photos.forms')


class GalleryForm(forms.ModelForm):
    class Meta:
        fields = '__all__'
        widgets = {
            'images': forms.SelectMultiple,
        }

    def __init__(self, *args, **kwargs):
        super(GalleryForm, self).__init__(*args, **kwargs)
        self.fields['images'].choices = [(image.id, mark_safe('%sqq%sqq%s' % (image.title, image.get_image_by_size('tn'), image.pk))) for image in LuxImage.objects.all()[:40]]
        self.fields['images'].allow_tags = True


from django.utils.safestring import mark_safe

class ImageChoiceField(forms.ModelMultipleChoiceField):

    def label_from_instance(self, obj):

        return mark_safe('%sqq%sqq%s' % (obj.title, obj.get_image_by_size('tn'), obj.pk))



class FKGalleryForm(forms.ModelForm):
    class Meta:
        fields = '__all__'
        widgets = {
            'image': ImageChoiceField(queryset=LuxImage.objects.all()),
        }

    def __init__(self, *args, **kwargs):
        super(FKGalleryForm, self).__init__(*args, **kwargs)
        self.fields['image'].choices = [(o.id, str(o.image.url)) for o in LuxImage.objects.all()]
        self.fields['image'].allow_tags = True

class UploadZipForm(forms.Form):
    """
        Handles the uploading of a gallery of photos packed in a .zip file
        Creates Gallery object, adds photos with all metadata that's available
    """
    zip_file = forms.FileField()
    title = forms.CharField(label=_('Gallery Title'), max_length=250)
    slug = forms.SlugField(label=_('Gallery Slug'))
    desc = forms.CharField(label=_('Gallery Caption'), widget=forms.Textarea, required=False)
    date = forms.SplitDateTimeField(label=_('Date'), widget=widgets.AdminSplitDateTime)
    is_public = forms.BooleanField(label=_('Is public'), initial=True, required=False, help_text=_('Show on site'))

    def clean_zip_file(self):
        """Open the zip file a first time, to check that it is a valid zip archive.
        We'll open it again in a moment, so we have some duplication, but let's focus
        on keeping the code easier to read!
        """
        zip_file = self.cleaned_data['zip_file']
        try:
            zip = zipfile.ZipFile(zip_file)
        except BadZipFile as e:
            raise forms.ValidationError(str(e))
        bad_file = zip.testzip()
        if bad_file:
            zip.close()
            raise forms.ValidationError('"%s" in the .zip archive is corrupt.' % bad_file)
        zip.close()  # Close file in all cases.
        return zip_file

    def clean_title(self):
        title = self.cleaned_data['title']
        if title and LuxGallery.objects.filter(title=title).exists():
            raise forms.ValidationError(_('A gallery with that title already exists.'))
        return title

    def clean(self):
        cleaned_data = super(UploadZipForm, self).clean()
        if not self['title'].errors:
            # If there's already an error in the title, no need to add another
            # error related to the same field.
            if not cleaned_data.get('title', None) and not cleaned_data['gallery']:
                raise forms.ValidationError(
                    _('Select an existing gallery, or enter a title for a new gallery.'))
        return cleaned_data

    def save(self, request=None, zip_file=None):
        if not zip_file:
            zip_file = self.cleaned_data['zip_file']

        gallery, created = LuxGallery.objects.get_or_create(
            title=self.cleaned_data['title'],
            description=self.cleaned_data['desc'],
            slug=self.cleaned_data['slug'],
            pub_date=self.cleaned_data['date'],
            is_public=self.cleaned_data['is_public']
        )
        zipper = zipfile.ZipFile(zip_file)
        count = 1
        for filename in sorted(zipper.namelist()):
            f, file_extension = os.path.splitext(filename)
            logger.debug('Reading file "{0}".'.format(filename))
            if filename.startswith('__') or filename.startswith('.'):
                logger.debug('Ignoring file "{0}".'.format(filename))
                continue
            if os.path.dirname(filename):
                logger.warning('Ignoring file "{0}" as it is in a subfolder; all images should be in the top '
                               'folder of the zip.'.format(filename))
                if request:
                    messages.warning(request,
                                     _('Ignoring file "{filename}" as it is in a subfolder').format(filename=filename), fail_silently=True)
                continue
            data = zipper.read(filename)

            if not len(data):
                logger.debug('File "{0}" is empty.'.format(filename))
                continue

            fn, file_extension = os.path.splitext(filename)
            if file_extension != ".mp4":
                # Basic check that we have a valid image.
                try:
                    file = BytesIO(data)
                    opened = Image.open(file)
                    opened.verify()
                except Exception:
                    # Pillow (or PIL) doesn't recognize it as an image.
                    # If a "bad" file is found we just skip it.
                    # But we do flag this both in the logs and to the user.
                    logger.error('Could not process file "{0}" in the .zip archive.'.format(filename))
                    if request:
                        messages.warning(request,
                                         _('Could not process file "{0}" in the .zip archive.').format(
                                             filename),
                                         fail_silently=True)
                    continue
            image = LuxImage(
                pub_date=datetime.datetime.now()
            )
            contentfile = ContentFile(data)
            image.image.save(filename, contentfile)
            if file_extension != ".mp4":
                img = Image.open(image.image.path)
                if img.size[0] > img.size[1]:
                    image.sizes.add(LuxImageSize.objects.get(width=2560))
                    image.sizes.add(LuxImageSize.objects.get(width=1170))
                    image.sizes.add(LuxImageSize.objects.get(width=720))
                if img.size[1] > img.size[0]:
                    image.sizes.add(LuxImageSize.objects.get(height=1600))
                    image.sizes.add(LuxImageSize.objects.get(height=800))
                    image.sizes.add(LuxImageSize.objects.get(height=460))
                image.save()
            gallery.images.add(image)

        zipper.close()

        if request:
            messages.success(request, _('The photos have been uploaded'))