"""Custom image field for other models to use

This is the whole point of the image app.  Other apps
should be able to do:


class Club(models.Model):
    name = models.CharField(max_length=50)
    sport = models.CharField(max_length=20, default="football", blank=True)
    image = imadj.models.ImageField(size="team,
                                    tag="blah")
    details = models.TextField(blank=True)

The image can be mimicked almost perfectly well by the following...

    image = models.ForeignKey("imadj.Image",
                              limit_choices_to={'size__id':'team'},
                              blank=True,
                              null=True,
                              help_text="Image Gallery image")

However it's nicer to wrap this up and offer a nice display widget.

http://blog.elsdoerfer.name/2008/01/08/fuzzydates-or-one-django-model-field-multiple-database-columns/

"""


from django.db.models import ForeignKey, Field, TextField

from django.forms import widgets, fields
from django.forms.util import ValidationError
from django.utils.functional import curry
from django.utils.safestring import mark_safe 

from django.conf import settings

from models import Image, Size, Tag, THUMB_URL




class ImageInput(widgets.TextInput):
    class Media:
        css = {
            "all": (
                "imadj/rsrc/css/thickbox.css", 
                "imadj/rsrc/css/imagepicker.css",
                )
            }
        js = (
            "imadj/rsrc/js/jquery.js",
            "imadj/rsrc/js/imagepicker.js",
            "imadj/rsrc/js/thickbox.js",
            )





class ImageField(ForeignKey):
    """This field can be used in other models to define the allowed images.
    
    e.g.
    class Attraction(models.Model):
        name = (....)
        image = ImageField(size="attraction", blank=True, null=True)
    
    size is required.
    tag is optional.
    use 'null=False' if you want to make it required'
    """
    def __init__(self, *args, **kwargs):
        #the code below does the right thing when using the
        #native Django behaviour - a dropdown of alternative
        #images you can use.  But we could possibly chuck it
        #as we are not using dropdowns.
        
        #create the right ForeignKey restrictions
        limit = {}
        self.size = kwargs.pop('size', None)
        if self.size:
            limit['size__id'] = self.size
        self.tag = kwargs.pop('tag', None)
        if self.tag:
            limit['tags__id'] = self.tag
            
        self.required = not kwargs.get('blank', False)
        
        #print 'initialising field, required = %s' % self.required
        
        #super doesn't work for some reason.  Modify it after.
        to = kwargs.pop('to',"imadj.Image")
        ForeignKey.__init__(self, to, **kwargs)
    
        #What follows is handy to get a dropdown interface,
        #although we aren't using that just now.
        if limit:
            self.rel.limit_choices_to = limit

    def formfield(self, **kwargs): 
        "Return the form field to use"
        kwargs['form_class'] = ImadjFormField
        kwargs['size'] = self.size
        kwargs['tag'] = self.tag
        kwargs['required'] = self.required
        kwargs['widget'] = ImageInput()
        kwargs['widget'].choices = []
        return super(ImageField, self).formfield(**kwargs)




    def contribute_to_class(self, cls, name):
        """For each image on a model, you get extra model methods.
        """
        tagFunc = curry(_get_editable_img_tag, fld=self, name=name)
        # Make it safe for contrib.admin
        tagFunc.allow_tags = True
        methodName = 'get_%s_editor' % name
        setattr(cls, methodName, tagFunc)

        super(ImageField, self).contribute_to_class(cls, name)
        
        

def _get_editable_img_tag(model, fld, name):
    """Creates an editable img tag you can use in your templates.
    
    This will appear as model.get_NAME_editor() 
    with NAME set to the name of the image field.
    
    this needs to pull together info from 
     - the Field - size, required etc
     - the Model (e.g. the football club)
     - the Image being requested
    
    """ 
    
    classes = ['imadjselect', 'size:%s' % fld.size]
    if fld.tag:
        classes.append('tag:%s' % fld.tag)
    if fld.required:
        classes.append('required')
    cssClass = ' '.join(classes)
    
    
    #now for Image src
    image = getattr(model, name)
    
    if image:
        src = image.get_absolute_url()
    else:
        src = 'None'  #how to signal None?
        
    #alt tag if we have a description

    alt_attr = ''   #may not be used            
    if image:
        if image.description:
            alt_attr = ' alt="%s"' % image.description
    
    #id encodes the model name, key and fieldname
    id = '%s:%s:%s_id' % (
        model.__class__.__name__.lower(),
        model.id,
        name
        )
        
    width = image.size.width
    height = image.size.height
    
    tag =   '<img id="%s" src="%s" class="%s"%s width="%s" height="%s"/>' % (
        id, src, cssClass, alt_attr, width, height)
    return mark_safe(tag)
    
    
class ImadjFormField(fields.CharField):
    "This handles checking and html generation"

    def __init__(self, *args, **kwargs):
        self.size = kwargs.pop('size')
        self.tag = kwargs.pop('tag')
        for arg in ['queryset','to_field_name']:
            if kwargs.has_key(arg):
                del kwargs[arg]


        super(ImadjFormField, self).__init__(*args, **kwargs)

    def widget_attrs(self, widget):
        #this customises the text field
        
        classes = ['imadjselect']
        classes.append('size:%s' % self.size)
        if self.tag:
            classes.append('tag:%s' % self.tag)
        if self.required:
            classes.append('required')
        classText = ' '.join(classes)
        return {'class': classText, 'size':50}

    def clean(self, value):
        """Checks the text is a valid image matching constraints.
        
        Strings are assumed to be image ids.
        """
    
        #let the parent class check for nulls
        value = super(ImadjFormField, self).clean(value)
        if not value:
            return None

 
        #Any values must be an image id; but don't barf
        #on unicode or spaces.
        if type(value) == type(u''):
            value = value.encode('ascii')
        value = value.strip()
        
        
        try:
            img = Image.objects.get(id=value)
        except Image.DoesNotExist:
            raise ValidationError("Given value does not refer to a valid image")
        if img.size_id != self.size:
            raise ValidationError("Expected image of size '%s', but got '%s'" % (
                self.size, img.size_id))

        if self.tag:
            if self.tag not in img.tag_text.split():
                raise ValidationError("Image lacks tag '%s'" % self.tag)
                

        #OK, it meets the foreign key constraint
        return img

