import os, posixpath, glob

from django.db import models
from django import forms
from django.core import validators
from django.dispatch import dispatcher
from django.db.models import signals

from rlextra.utils import pdf2jpeg
from rldjango.common.validators import ArtworkValidator, ArtworkConvertor, formatFileSize, replaceFileExt

from rlextra.thirdparty.verdjnlib.fields import ImageField, DeleteCheckbox


class NoArtworkError(Exception):
    pass


class ArtworkUploadField(forms.FileUploadField):
    """Artwork field that supports bitmaps (those supported by PIL), PDF, and EPS.

    The user can only upload one file per field, so they have to choose EITHER
    a bitmap, OR PDF, OR EPS.  PDF or EPS is preferred, since then we have a
    vector format to use in PDFs we generate, and this field can generate
    bitmaps from the vector for use in web pages &c.

    XXX don't allow too-big files
    """

    def __init__(self, *args, **kwargs):
        forms.FileUploadField.__init__(self, *args, **kwargs)
        self.validator_list.insert(0, self.validate)

    def validate(self, field_data, all_data):
        # XXX Since the validator just does the conversion and raises
        # ValidationError if that fails, would be nice to keep the files the
        # conversion process generates (JPEG, PDF) and use them in
        # ArtworkField._saveFile, but not obvious how to do that -- we don't
        # have the model field from which to get modelField.upload_to (for good
        # reason), but not sure where to cache the data so the model field can
        # save it later...
        av = ArtworkValidator()
        try:
            av.validate(field_data, all_data)
        except validators.ValidationError, e:
            raise validators.CriticalValidationError, e.messages
        pass


class ArtworkField(ImageField):
    # XXX
    # thumbnails
    # commit newlines change to pdf2jpeg
    # epsConvert doesn't like one of these five or six files: \\shaolin\c\jjlee\maps\*.eps
    # clean up (don't forget pdffield etc. -- use get_internal_type() to avoid
    #  monkeypatching, move stuff out of .render into a pdf.html admin template)

    def __init__(self, *args, **kwds):
        ImageField.__init__(self, *args, **kwds)
        self.pdfFileName = None
        self.parent_pk = "UNDEFINED"

    def get_internal_type(self):
        return 'ImageField'

    def _update_parent_pk(self, instance=None):
        self.parent_pk = instance._get_pk_val()

    def get_manipulator_field_objs(self):
        if self.null:
            return [ArtworkUploadField, forms.HiddenField, DeleteCheckbox]
        else:
            return [ArtworkUploadField, forms.HiddenField]

    def _saveFile(self, model, filename, raw_contents):
        from django.conf import settings
        directory = self.get_directory_name()
        try: # Create the date-based directory if it doesn't exist.
            os.makedirs(os.path.join(settings.MEDIA_ROOT, directory))
        except OSError: # Directory probably already exists.
            pass

        # figure out what type of file has been uploaded, write out PDF and
        # JPEG if needed, and compute the filename from the results
        outDir = os.path.join(settings.MEDIA_ROOT, self.upload_to)
        basename = '%s-%s' % (self.parent_pk, self.get_attname())

##         # delete old files
##         for fn in oldFileFns:
##             os.remove(fn)

        # write out .pdf and .jpg (if appropriate)
        ac = ArtworkConvertor(outDir, outFilename=basename)
        ac.convert(raw_contents)
        filename = os.path.join(self.get_directory_name(),
                                '%s.%s' % (basename, ac.fileType))

        setattr(model, self.attname, filename)

        # write uploaded file to disk
        fullFilename = model._get_FIELD_filename(self)
        fp = open(fullFilename, 'wb')
        fp.write(raw_contents)
        fp.close()

        # Save the width and/or height, if applicable.
        if isinstance(self, ImageField) and (self.width_field or self.height_field):
            from django.utils.images import get_image_dimensions
            width, height = get_image_dimensions(fullFilename)
            if self.width_field:
                setattr(model, self.width_field, width)
            if self.height_field:
                setattr(model, self.height_field, height)

        # Save the object, because it has changed.
        model.save()

    def contribute_to_class(self, cls, name):
        super(ArtworkField, self).contribute_to_class(cls, name)
        # override e.g. SomeModel.save_bitmapLogo_file()
        setattr(cls, 'save_%s_file' % self.name, lambda model, filename, raw_contents: self._saveFile(model, filename, raw_contents))
        setattr(cls, 'write_%s_jpeg' % self.name, lambda model, size=None: self._writeJpeg(size))
        dispatcher.connect(self._update_parent_pk, signals.post_save, sender=cls)

    def _getArtworkFilename(self, basename=None):
        from django.conf import settings
        if getattr(self, self.attname): # value is not blank
            if basename is None:
                basename = self.upload_to
            return os.path.join(settings.MEDIA_ROOT, basename)
        return ''

    def _filenameFromFormat(self, format):
        return self._getArtworkFilename(os.path.join(self.get_directory_name(),
                                                     '%s.%s' % (basename, '*')))

    def _availableFormats(self):
        filesGlob = self._getArtworkFilename(os.path.join(self.get_directory_name(),
                                                          '%s.%s' % (basename, '*')))
        return [os.path.splitext(fn)[1][1:] for fn in glob.glob(model._get_FIELD_filename(self))]

    def _writeJpeg(self, size):
        artworkFilename = self._getArtworkFilename()
        if not artworkFilename:
            raise NoArtworkError()
        basename, ext = os.path.splitext(artworkFilename)
        format = ext[1:]
        jpegFilename = basename+'-%s-%s.jpg' % size

        if os.path.exists(jpegFilename):
            return jpegFilename

        if format in ['eps', 'pdf']:
            pdfConvert(
                artworkFilename,
                size=size,
                outDir=os.path.dirname(artworkFilename),
                outfile=jpegFilename,
                device='jpeg',
                )
        else:
            artworkFilename = self._filenameFromFormat(best)
            from rlextra.thirdparty.verdjnlib.fields import photofield
            f = open(artworkFilename, 'rb')
            data = photofield.PhotoField.resize(f.read(),
                                                size[0],
                                                size[1],
                                                mode=photofield.FIT,
                                                quality=None
                                                )
            f.close()
            f = open(jpegFilename, 'wb')
            try:
                f.write(data)
            finally:
                f.close()

        return jpegFilename

    def thumbnailUrl(self):
        # XXXX oops can't get URL from here!
        return self._writeJpeg((100, 100))

    def artWorkFilenameForPdf(self, size):
        raise NotImplementedError("XXX")

    def bitmapUrlForWebPage(self, size):
        raise NotImplementedError("XXX")

    # Some helper methods for display.  XXX Use in templates/widget/image.html .

    def getAdminDisplayWidth(self):
        if self.width < 200:
            return self.width
        else:
            return 200

    def getAdminDisplayHeight(self):
        if self.width < 200:
            return self.height
        else:
            return int(self.height * 200.0 / self.width)
