import re
from django.db.models import fields
from django.template.defaultfilters import slugify

# -- Authors -----
# Combined from following two snippets:
# crucialfelix (_unique_slugify) http://www.djangosnippets.org/snippets/1321/
# GaretJax (AutoSlugField) http://www.djangosnippets.org/snippets/728/ 
# 
# Improved from those by Ciantic, 2010.

def _unique_slugify(instance, value, slug_field_name='slug', queryset=None,
                    slug_separator='-'):
    """
    Calculates a unique slug of ``value`` for an instance.

    :param slug_field_name: Should be a string matching the name of the field to
        store the slug in (and the field to check against for uniqueness).

    :param queryset: usually doesn't need to be explicitly provided - it'll
        default to using the ``.all()`` queryset from the model's default
        manager.
    
    """
    slug_field = instance._meta.get_field(slug_field_name)
    
    slug_len = slug_field.max_length

    # Sort out the initial slug. Chop its length down if we need to.
    slug = slugify(value)
    if slug_len:
        slug = slug[:slug_len]
    slug = _slug_strip(slug, slug_separator)
    original_slug = slug

    # Create a queryset, excluding the current instance.
    if queryset is None:
        queryset = instance.__class__._default_manager.all()
        if instance.pk:
            queryset = queryset.exclude(pk=instance.pk)

    # Find a unique slug. If one matches, at '-2' to the end and try again
    # (then '-3', etc).
    next = 2
    while not slug or queryset.filter(**{slug_field_name: slug}):
        slug = original_slug
        end = '-%s' % next
        if slug_len and len(slug) + len(end) > slug_len:
            slug = slug[:slug_len-len(end)]
            slug = _slug_strip(slug, slug_separator)
        slug = '%s%s' % (slug, end)
        next += 1

    setattr(instance, slug_field.attname, slug)
    return slug

def _slug_strip(value, separator=None):
    """
    Cleans up a slug by removing slug separator characters that occur at the
    beginning or end of a slug.

    If an alternate separator is used, it will also replace any instances of the
    default '-' separator with the new separator.
    
    """
    if separator == '-' or not separator:
        re_sep = '-'
    else:
        re_sep = '(?:-|%s)' % re.escape(separator)
        value = re.sub('%s+' % re_sep, separator, value)
    return re.sub(r'^%s+|%s+$' % (re_sep, re_sep), '', value)

class AutoSlugField(fields.SlugField):
    """Auto slug field, creates unique slug for model."""
    
    def __init__(self, prepopulate_from, *args, **kwargs):
        """Create auto slug field.
        
        If field is unique, the uniqueness of the slug is ensured from existing
        slugs by adding extra number at the end of slug.
        
        If field has slug given, it is used instead. If you want to re-generate
        the slug, just set it :const:`None` or :const:`""` so it will be re-
        generated automatically.
        
        :param prepopulate_from: Must be assigned to list of field names which
            are used to prepopulate automatically. 
        
        :type prepopulate_from: sequence
        
        """
        self.prepopulate_separator = kwargs.get("prepopulate_separator", u"-")
        self.prepopulate_from = prepopulate_from
        kwargs["blank"] = True
        super(fields.SlugField, self).__init__(*args, **kwargs)
    
    def pre_save(self, model_instance, add): #@UnusedVariable
        """Pre-save event"""
        current_slug = getattr(model_instance, self.attname)
        
        # Use current slug instead, if it is given.
        # Assumption: There are no empty slugs.
        if not (current_slug is None or current_slug == ""):
            slug = current_slug
        else:
            slug = self.prepopulate_separator.\
                        join(unicode(getattr(model_instance, prepop)) 
                             for prepop in self.prepopulate_from)
        
        if self.unique:
            return _unique_slugify(model_instance, value=slug, 
                                   slug_field_name=self.attname)
        else:
            return slugify(slug)[:self.max_length]