from django.core import meta
from django.core.validators import *

#from django.core.cache import cache
# Create your models here.

# roomy model
# Based on the PG schema from Ralph
EVENT_TYPE_ROLES = (
    ('T', 'Basic Event'),
    ('F', 'Feature Event (effects all rooms)'),
    ('S', 'Service (note when event starts and stops)'),
    ('H', 'Administrative (hidden)'),
)

class InvalidIfFieldIsNot:
    def __init__(self, field_name, field_value, error_message=None):
        self.field_name = field_name
        self.field_value = field_value
        self.error_message = error_message or lazy_inter(gettext_lazy(
            "This field can not be set if %(field)s is not %(value)s"), {
                'field':   field_name,
                'value':   field_value})
        self.always_test = True

    def __call__(self, field_data, all_data):
        if (field_data and self.field_name in all_data and
            all_data[self.field_name] != self.field_value):
            raise ValidationError(self.error_message)

class EventType(meta.Model):
    name   = meta.CharField("Name", maxlength=20, unique=True)
    role   = meta.CharField("Class", maxlength=1, choices=EVENT_TYPE_ROLES,
                            null=False, blank=False,
                            default=EVENT_TYPE_ROLES[0][0])
    prefix = meta.BooleanField("Prefix talk with type", default=False)
    startlabel = meta.CharField('Start Postfix', maxlength=20,
                                null=False, blank=True, default="",
                                validator_list=[
                                    RequiredIfOtherFieldEquals('role', 'S',
                                        "This field must be given if Class "
                                        "is Service."),
                                    InvalidIfFieldIsNot('role', 'S',
                                        "This field can not be given if Class "
                                        "is not Service.")],
                                help_text="e.g. 'starts', 'begins', 'opens'\n"
                                          " (only valid with Service types)")
    stoplabel  = meta.CharField('Stop Postfix', maxlength=20,
                                null=False, blank=True, default="",
                                validator_list=[
                                    RequiredIfOtherFieldEquals('role', 'S',
                                        "This field must be given if Class "
                                        "is Service."),
                                    InvalidIfFieldIsNot('role', 'S',
                                        "This field can not be given if Class "
                                        "is not Service.")],
                                help_text="e.g. 'stops', 'ends', 'closes'\n"
                                          " (only valid with Service types)")
    class META:
        admin = meta.Admin(
            list_display = ('name', 'role', 'prefix'),
            ordering = ('name', 'role'),
            )

    def __repr__(self):
        return self.name


def defauilt_eventtype_id():
    from django.models.roomy import eventtypes
    try:
        data = eventtypes.get_list()
    except:
        return 1
    if not data:
        basetype = eventtypes.EventType(name="Talk")
        basetype.save()
        return basetype.id
    return data[0].id

class Room(meta.Model):
    title = meta.CharField(maxlength=50, unique=True)

    def __repr__(self):
        return "%s" % (self.title)

    class META:
        admin = meta.Admin(
            list_display = ('title', ),
            ordering = ('title', ),
            )

class Presenter(meta.Model):
    name = meta.CharField(maxlength=50, unique=False)
    email = meta.EmailField(blank=True, null=False)
    phone = meta.PhoneNumberField(blank=True, null=False)
    affiliation = meta.CharField(blank=True, maxlength=50,
                                 unique=False, null=True)

    def __repr__(self):
        return "%s" % (self.name)

    class META:
        admin = meta.Admin(
            list_display = ('name', 'affiliation'),
            ordering = ('name', 'affiliation'),
            )
        unique_together = (
            ("name", "affiliation"),
            )

def validateUrlIsPyConSite(field_data, all_data):
    if (field_data and
        not field_data.lower().startswith("http://us.pycon.org/") and
        not field_data.lower().startswith("http://wiki.python.org/")):
        raise ValidationError, (
            "Only urls for 'us.pycon.org' and 'wiki.python.org' are allowed.")

class Talk(meta.Model):
    title = meta.CharField(maxlength=150, unique=True)
    url = meta.CharField(maxlength=150, blank=True,
                         validator_list=[validateUrlIsPyConSite,isExistingURL])
    duration =  meta.PositiveSmallIntegerField()
    presenter = meta.ForeignKey(Presenter, null=True, blank=True)
    description  = meta.TextField(null=False, blank=True,
                                  validator_list=[isValidHTML])

    def _manipulator_validate_duration(self, field_data, all_data):
        from django.core import validators
        if (int(field_data) > 480):
            raise validators.ValidationError, "Duration too long."

    def __repr__(self):
        from django.models.roomy import schedules
        try:
            etype = self.get_schedule().get_role()
            if etype.prefix:
                return "%s: %s" % (repr(etype), self.title)
        except schedules.ScheduleDoesNotExist: pass
        return "%s" % (self.title)
    __repr__.short_description='Title'

    def room(self):
        from django.models.roomy import schedules, rooms
        try:
            sched = self.get_schedule()
        except schedules.ScheduleDoesNotExist:
            return ""
        try:
            room = sched.get_room()
        except rooms.RoomDoesNotExist:
            return ""
        except Exception, e:
            return str(e)
        return room.title

    def startdate(self):
        from django.models.roomy import schedules
        try:
            sched = self.get_schedule()
        except schedules.ScheduleDoesNotExist:
            return ""
        return sched.startdate

    def starttime(self):
        from django.models.roomy import schedules
        try:
            sched = self.get_schedule()
        except schedules.ScheduleDoesNotExist:
            return ""
        return sched.starttime

    def gettype(self):
        from django.models.roomy import schedules
        try:
            sched = self.get_schedule()
        except schedules.ScheduleDoesNotExist:
            return ""
        return repr(sched.get_role())
    gettype.short_description="Type"

    class META:
      admin = meta.Admin(
        ordering = ('title', ),
        list_display = ('__repr__', 'duration', 'room', 'gettype', 'startdate',
                        'starttime', 'presenter'),
        list_filter = ('title', ),
        search_fields = ('title',)
            )


## A schedule item has a start, stop, room and talk
def validate_starttime(field_data, all_data):
    from django.core import validators
    from django.models.roomy import schedules, rooms, talks, eventtypes
    import datetime
    def raise_conflict(sched):
        msg = "Bad start time, %s allocated for %s running from %s until %s"%\
            (sched.getroom(),
            repr(sched.get_talk()),
            sched.starttime,
            sched.stop)
        #raise RuntimeError,  msg
        raise validators.ValidationError,  msg

    in_tabular = 'schedule.0.starttime' in all_data
    starttime = field_data
    not_this_talk = {}
    talk = None
    if in_tabular:
        try:
            talk = talks.get_object(title__exact=all_data['title']).id
            not_this_talk = { 'talk__id__ne': talk }
        except talks.TalkDoesNotExist:
           if 'schedule.0.id' in all_data and all_data['schedule.0.id']:
               sched = schedules.get_object(pk=all_data['schedule.0.id'])
               talk = sched.get_talk().id
               not_this_talk = {'talk__id__ne': talk }
    else:
        talk = all_data['talk']
        not_this_talk = { 'talk__id__ne': talk }

    startdate = in_tabular and all_data['schedule.0.startdate'] or all_data['startdate']

    if in_tabular:
        room = all_data['schedule.0.room']
    else:
        room = all_data['room']

    role = in_tabular and all_data['schedule.0.role'] or all_data['role']
    duration = in_tabular and int(all_data['duration']) or talks.get_object(pk=talk).duration

    year, month, day = startdate.split('-')
    timed = starttime.split(':')
    timed.append(0)
    hours, mins, secs = timed[:3]
    starttime = datetime.datetime(int(year), int(month),
                                  int(day), int(hours), int(mins),
                                  int(secs), 0)

    stop = starttime + datetime.timedelta(minutes=duration)

    # treat each event which does not have an assigned room
    # as if it is in its own uniq 'virtual' room
    # This will not stop comflicts with 'Feature' events
    if room:
        # Find all other talks that -start- within my talk interval.
        crash = schedules.get_list(
            room__pk=room,
            startdate__exact = starttime.date(),
            starttime__gte=starttime.time(),
            starttime__lt = stop.time(),
            **not_this_talk)
        if len(crash):
            raise_conflict(crash[0])

        # Find all other talks that -stop- within my talk interval.
        crash = schedules.get_list(
            room__pk=room,
            startdate__exact = starttime.date(),
            stop__gt = starttime.time(),
            stop__lt = stop.time(),
            talk__id__ne=talk)

        if len(crash):
            raise_conflict(crash[0])

    ## only for basic events
    if eventtypes.get_object(pk=role).role == 'T':
        # Find all other talks that -start- within my talk interval
        #      in any room. All basic and feature talks anyway.
        crash = schedules.get_list(
            role__role__in=('F'),
            startdate__exact = starttime.date(),
            starttime__gte=starttime.time(),
            starttime__lt = stop.time(),
            **not_this_talk)
        if len(crash):
            raise_conflict(crash[0])

        # Find all other talks that -stop- within my talk interval
        #      in any room All basic and feature talks anyway.
        crash = schedules.get_list(
            role__role__in=('F'),
            startdate__exact = starttime.date(),
            stop__gt = starttime.time(),
            stop__lt = stop.time(),
            **not_this_talk)
        if len(crash):
            raise_conflict(crash[0])

    ## only for 'Feature' Events
    if eventtypes.get_object(pk=role).role == 'F':
        # Find all other talks that -start- within my talk interval in any
        #      'Feature' EventType
        crash = schedules.get_list(
            role__role__in=('T', 'F'),
            startdate__exact = starttime.date(),
            starttime__gte=starttime.time(),
            starttime__lt = stop.time(),
            **not_this_talk)
        if len(crash):
            raise_conflict(crash[0])

        # Find all other talks that -stop- within my talk interval in any
        #      'Feature' EventType
        crash = schedules.get_list(
            role__role__in=('T', 'F'),
            startdate__exact = starttime.date(),
            stop__gt = starttime.time(),
            stop__lt = stop.time(),
            **not_this_talk)
        if len(crash):
            raise_conflict(crash[0])

class Schedule(meta.Model):

    starttime = meta.TimeField(core=True,
                               validator_list=[validate_starttime])
    startdate = meta.DateField(core=True)
    stop = meta.TimeField(editable=False)
    room = meta.ForeignKey(Room, null=True, blank=True, verbose_name='Room')
    role = meta.ForeignKey(EventType, verbose_name='Event Type',
                           null=False, blank=False,
                           default=defauilt_eventtype_id())
    talk = meta.ForeignKey(Talk, unique=True, verbose_name="Title",
                           edit_inline=meta.TABULAR, max_num_in_admin=1,
                           num_in_admin=1)

    class META:
        admin = meta.Admin(
            ordering = ('startdate', 'starttime'),
            list_display = ('startdate', 'starttime', 'getduration',
                            'getroom', 'talk'),
            list_filter = ('startdate', 'room', 'role'),)
        module_constants = { 'EVENT_TYPE_ROLES': EVENT_TYPE_ROLES }
    def __repr__(self):
        room = self.getroom()
        if room:
            return "%s - %s"%(self.get_talk(), room )
        else:
            return repr(self.get_talk())

    def gettitle(self):
        return repr(self.get_talk())

    def getroom(self):
        try:
            return self.get_room()
        except: pass
        return ""
    getroom.short_description='Room'

    def getduration(self):
        return self.get_talk().duration
    getduration.short_description='Duration'

    def is_basic(self):
        return self.get_role().role == 'T'
    def is_feature(self):
        return self.get_role().role == 'F'
    def is_service(self):
        return self.get_role().role == 'S'
    def is_hidden(self):
        return self.get_role().role == 'H'
    def is_open(self):
        return self.get_role().name == "Open Space Talk"
    def is_unassigned_open(self):
        return self.is_open() and self.get_talk().title.startswith("Unassigned: ")
    def get_role_type_desc(self):
        return EVENT_TYPE_ROLES[self.get_role().role]

    def _pre_save(self):
        from django.models.roomy import talks
        # create datetime from date and time separated
        startdatetime = datetime.datetime(self.startdate.year, self.startdate.month,
          self.startdate.day, self.starttime.hour, self.starttime.minute, 0)
        duration = self.get_talk().duration
        stop = startdatetime + datetime.timedelta(minutes=duration)
        self.stop = stop.time()
