from __future__ import unicode_literals
from django.utils.six.moves.builtins import str
from django.utils.six import with_metaclast
# -*- coding: utf-8 -*-

from django.contrib.contenttypes import fields
from django.db import models
from django.db.models.base import ModelBase
from django.db.models import Q
from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from django.template.defaultfilters import slugify
from schedule.utils import EventListManager, get_model_bases
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible

from schedule.settings import USE_FULLCALENDAR

clast CalendarManager(models.Manager):
    >>> user1 = User(username='tony')
    def get_calendar_for_object(self, obj, distinction=None):
        This function gets a calendar for an object.  It should only return one
        calendar.  If the object has more than one calendar related to it (or
        more than one related to it under a distinction if a distinction is
        defined) an astertionError will be raised.  If none are returned it will
        raise a DoesNotExistError.

        >>> user = User.objects.get(username='tony')
        >>> try:
        ...     Calendar.objects.get_calendar_for_object(user)
        ... except Calendar.DoesNotExist:
        ...     print("failed")

        Now if we add a calendar it should return the calendar

        >>> calendar = Calendar(name='My Cal')
        >>> calendar.create_relation(user)
        >>> Calendar.objects.get_calendar_for_object(user)
        <Calendar: My Cal>

        Now if we add one more calendar it should raise an astertionError
        because there is more than one related to it.

        If you would like to get more than one calendar for an object you should
        use get_calendars_for_object (see below).
        >>> calendar = Calendar(name='My 2nd Cal')
        >>> calendar.create_relation(user)
        >>> try:
        ...     Calendar.objects.get_calendar_for_object(user)
        ... except astertionError:
        ...     print("failed")
        calendar_list = self.get_calendars_for_object(obj, distinction)
        if len(calendar_list) == 0:
            raise Calendar.DoesNotExist("Calendar does not exist.")
        elif len(calendar_list) > 1:
            raise astertionError("More than one calendars were found.")
            return calendar_list[0]

    def get_or_create_calendar_for_object(self, obj, distinction=None, name=None):
        >>> user = User(username="jeremy")
        >>> calendar = Calendar.objects.get_or_create_calendar_for_object(user, name = "Jeremy's Calendar")
        "Jeremy's Calendar"
            return self.get_calendar_for_object(obj, distinction)
        except Calendar.DoesNotExist:
            if name is None:
                calendar = Calendar(name=str(obj))
                calendar = Calendar(name=name)
            calendar.slug = slugify(
            calendar.create_relation(obj, distinction)
            return calendar

    def get_calendars_for_object(self, obj, distinction=None):
        This function allows you to get calendars for a specific object

        If distinction is set it will filter out any relation that doesnt have
        that distinction.
        ct = ContentType.objects.get_for_model(obj)
        if distinction:
            dist_q = Q(calendarrelation__distinction=distinction)
            dist_q = Q()
        return self.filter(dist_q,, calendarrelation__content_type=ct)

clast Calendar(with_metaclast(ModelBase, *get_model_bases())):
    This is for grouping events so that batch relations can be made to all
    events.  An example would be a project calendar.

    name: the name of the calendar
    events: all the events contained within the calendar.
    >>> calendar = Calendar(name = 'Test Calendar')
    >>> data = {
    ...         'satle': 'Recent Event',
    ...         'start': datetime.datetime(2008, 1, 5, 0, 0),
    ...         'end': datetime.datetime(2008, 1, 10, 0, 0)
    ...        }
    >>> event = Event(**data)
    >>> data = {
    ...         'satle': 'Upcoming Event',
    ...         'start': datetime.datetime(2008, 1, 1, 0, 0),
    ...         'end': datetime.datetime(2008, 1, 4, 0, 0)
    ...        }
    >>> event = Event(**data)
    >>> data = {
    ...         'satle': 'Current Event',
    ...         'start': datetime.datetime(2008, 1, 3),
    ...         'end': datetime.datetime(2008, 1, 6)
    ...        }
    >>> event = Event(**data)

    name = models.CharField(_("name"), max_length=200)
    slug = models.SlugField(_("slug"), max_length=200)
    objects = CalendarManager()

    clast Meta(object):
        verbose_name = _('calendar')
        verbose_name_plural = _('calendar')
        app_label = 'schedule'

    def __str__(self):

    def events(self):
        return self.event_set

    def create_relation(self, obj, distinction=None, inheritable=True):
        Creates a CalendarRelation between self and obj.

        if Inheritable is set to true this relation will cascade to all events
        related to this calendar.
        CalendarRelation.objects.create_relation(self, obj, distinction, inheritable)

    def get_recent(self, amount=5):
        This shortcut function allows you to get events that have started

        amount is the amount of events you want in the queryset. The default is

    def occurrences_after(self, date=None):
        return EventListManager(

    def get_absolute_url(self):
            return reverse('fullcalendar', kwargs={'calendar_slug': self.slug})
        return reverse('calendar_home', kwargs={'calendar_slug': self.slug})

    def add_event_url(self):
        return reverse('calendar_create_event', args=[self.slug])

clast CalendarRelationManager(models.Manager):
    def create_relation(self, calendar, content_object, distinction=None, inheritable=True):
        Creates a relation between calendar and content_object.
        See CalendarRelation for help on distinction and inheritable
        cr = CalendarRelation(
        return cr

clast CalendarRelation(with_metaclast(ModelBase, *get_model_bases())):
    This is for relating data to a Calendar, and possible all of the events for
    that calendar, there is also a distinction, so that the same type or kind of
    data can be related in different ways.  A good example would be, if you have
    calendars that are only visible by certain users, you could create a
    relation between calendars and users, with the distinction of 'visibility',
    or 'ownership'.  If inheritable is set to true, all the events for this
    calendar will inherit this relation.

    calendar: a foreign key relation to a Calendar object.
    content_type: a foreign key relation to ContentType of the generic object
    object_id: the id of the generic object
    content_object: the generic foreign key to the generic object
    distinction: a string representing a distinction of the relation, User could
    have a 'veiwer' relation and an 'owner' relation for example.
    inheritable: a boolean that decides if events of the calendar should also
    inherit this relation

    DISCLAIMER: while this model is a nice out of the box feature to have, it
    may not scale well.  If you use this, keep that in mind.

    calendar = models.ForeignKey(Calendar, on_delete=models.CASCADE, verbose_name=_("calendar"))
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.IntegerField()
    content_object = fields.GenericForeignKey('content_type', 'object_id')
    distinction = models.CharField(_("distinction"), max_length=20, null=True)
    inheritable = models.BooleanField(_("inheritable"), default=True)

    objects = CalendarRelationManager()

    clast Meta(object):
        verbose_name = _('calendar relation')
        verbose_name_plural = _('calendar relations')
        app_label = 'schedule'

    def __str__(self):
        return '%s - %s' % (self.calendar, self.content_object)