import warnings

from datetime import datetime

from django.db import models
from django.db import IntegrityError
from django.template.defaultfilters import slugify
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _
from django.db.models.fields import FieldDoesNotExist
from django.core.exceptions import ImproperlyConfigured

from model_utils.managers import manager_from, InheritanceCastMixin, \
    QueryManager
from model_utils.fields import AutoCreatedField, AutoLastModifiedField, \
    StatusField, MonitorField

try:
    from django.utils.timezone import now as now
except ImportError:
    now = datetime.now


class InheritanceCastModel(models.Model):
    """
    An abstract base class that provides a ``real_type`` FK to ContentType.

    For use in trees of inherited models, to be able to downcast
    parent instances to their child types.

    Pending deprecation; use InheritanceManager instead.

    """
    real_type = models.ForeignKey(ContentType, editable=False, null=True)

    objects = manager_from(InheritanceCastMixin)

    def __init__(self, *args, **kwargs):
        warnings.warn(
            "InheritanceCastModel is pending deprecation. "
            "Use InheritanceManager instead.",
            PendingDeprecationWarning,
            stacklevel=2)
        super(InheritanceCastModel, self).__init__(*args, **kwargs)

    def save(self, *args, **kwargs):
        if not self.id:
            self.real_type = self._get_real_type()
        super(InheritanceCastModel, self).save(*args, **kwargs)

    def _get_real_type(self):
        return ContentType.objects.get_for_model(type(self))

    def cast(self):
        return self.real_type.get_object_for_this_type(pk=self.pk)

    class Meta:
        abstract = True


class TitleSlugModel(models.Model):
    """
    An abstract base class model that provides a ``title``
    and ``slug`` fields
    """
    name = models.CharField(max_length=100)
    slug = models.SlugField(max_length=105, unique=True) # Longer if it tacks on integer

    SLUG_MAX_RETRIES = 5 # How many integrity errors before failing

    def generate_slug(self):
        return slugify(self.name)

    def increment_slug(self, slug):
        """
        What to do when the slug is already found in the table

        Defaults to ordinary counter appending, but you may
        want to use a date or some other token
        """
        tries = getattr(self, '_slug_increment', 1)
        self._slug_increment = tries + 1
        return slugify('%s-%s' % (slug, self._slug_increment))

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = self.generate_slug()

        """
        Its not enough to check for the slug first because in rare
        cases it can be created between the get and the save below.

        Below, we attempt to save a unique slug but sometimes that
        doesn't go well. In that case, we retry on integrityerror a
        few times, incrementing our slug along the way.

        since our integrityerror might have other causes besides the slug,
        we don't let this runaway indefinitely and eventually raise

        If below fails, you must find another way of assigning
        a slug before calling model save()

        """

        tries = self.SLUG_MAX_RETRIES
        while True:
            try:
                super(TitleSlugModel, self).save(*args, **kwargs)
                break
            except IntegrityError, e:
                if tries == 0:
                    raise

                tries = tries - 1
                self.slug = self.increment_slug(self.generate_slug())

    class Meta:
        abstract = True


class TimeStampedModel(models.Model):
    """
    An abstract base class model that provides self-updating
    ``created`` and ``modified`` fields.

    """
    created = AutoCreatedField(_('created'))
    modified = AutoLastModifiedField(_('modified'))

    class Meta:
        abstract = True


class TimeFramedModel(models.Model):
    """
    An abstract base class model that provides ``start``
    and ``end`` fields to record a timeframe.

    """
    start = models.DateTimeField(_('start'), null=True, blank=True)
    end = models.DateTimeField(_('end'), null=True, blank=True)

    class Meta:
        abstract = True

class StatusModel(models.Model):
    """
    An abstract base class model with a ``status`` field that
    automatically uses a ``STATUS`` class attribute of choices, a
    ``status_changed`` date-time field that records when ``status``
    was last modified, and an automatically-added manager for each
    status that returns objects with that status only.

    """
    status = StatusField(_('status'))
    status_changed = MonitorField(_('status changed'), monitor='status')

    class Meta:
        abstract = True

def add_status_query_managers(sender, **kwargs):
    """
    Add a Querymanager for each status item dynamically.

    """
    if not issubclass(sender, StatusModel):
        return
    for value, name in getattr(sender, 'STATUS', ()):
        try:
            sender._meta.get_field(name)
            raise ImproperlyConfigured("StatusModel: Model '%s' has a field "
                                       "named '%s' which conflicts with a "
                                       "status of the same name."
                                       % (sender.__name__, name))
        except FieldDoesNotExist:
            pass
        sender.add_to_class(value, QueryManager(status=value))

def add_timeframed_query_manager(sender, **kwargs):
    """
    Add a QueryManager for a specific timeframe.

    """
    if not issubclass(sender, TimeFramedModel):
        return
    try:
        sender._meta.get_field('timeframed')
        raise ImproperlyConfigured("Model '%s' has a field named "
                                   "'timeframed' which conflicts with "
                                   "the TimeFramedModel manager."
                                   % sender.__name__)
    except FieldDoesNotExist:
        pass
    sender.add_to_class('timeframed', QueryManager(
        (models.Q(start__lte=now) | models.Q(start__isnull=True)) &
        (models.Q(end__gte=now) | models.Q(end__isnull=True))
    ))


models.signals.class_prepared.connect(add_status_query_managers)
models.signals.class_prepared.connect(add_timeframed_query_manager)

@@ -1798,33 +1798,32 @@
 bstract = True%0A%0A
-%0A
 class TitleSlugM
@@ -1813,28 +1813,24 @@
 %0Aclass Title
-Slug
 Model(models
@@ -1822,32 +1822,189 @@
 tleModel(models.
+Model):%0A    %22%22%22%0A    For good measure%0A    %22%22%22%0A    name = models.CharField(max_length=100)%0A    class Meta:%0A        abstract = True%0A%0A%0Aclass TitleSlugModel(Title
 Model):%0A    %22%22%22%0A
@@ -2098,52 +2098,8 @@
 %22%22%22%0A
-    name = models.CharField(max_length=100)%0A
     

split off title model because I always need this thing