"""Registration System Models
"""
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User, AnonymousUser, Group
from django.db.models import Q
from django.dispatch import dispatcher
from django import forms
from pycon.core import *
from django.core.cache import cache
from pycon.core.validators import *
from django.core.validators import *
from pycon.schedule.models import Presenter
from pycon.usermgr.models import HONORIFIC, SUFFIX
from django.db import transaction

import re, datetime

# Jason Williams (Jason Williams)	12-Feb-2007	$ 260 VGN MOK LOK NONE NONE
regre = re.compile("""^\s*
              (?P<full_name>                        # unique name for tshirt
               (?P<badge_name>.+?)                  # badge name
               (\s+\((?P<card_name>.+?)\))?)        # card name/organization?
              \s+                           
              (?P<date>\d\d-[a-zA-Z]{3,3}-200[67])  # date
              \s+
              \$\s+(?P<paid>\d+)                    # payment amount
              \s+
              (?P<flags>((?P<vegan>VGN\ )|          # flags (any order)
                         (?P<can_email>MOK\ )|
                         (?P<public>LOK\ )){0,3})?  
              ((\s*(NONE|(?P<tutorial_am>AM\d)))|   # tutorials (any order)
               (\s*(NONE|(?P<tutorial_pm>PM\d)))){2,2} 
              \s*$""", re.X + re.M)                     
# LD Landis (Lawrence D Landis)           	14-Dec-2006	$   0 2X
shirtre = re.compile("""^\s*
              (?P<full_name>                        # unique name for reg
               (?P<badge_name>.+?)                  # badge name
               (\s+\((?P<card_name>.+?)\))?)        # card name/organization?
              \s+                           
              (?P<date>\d\d-[a-zA-Z]{3,3}-200[67])  # date
              \s+
              \$\s+(\d+)                            # payment amount (ignored)
              \s+
              (?P<shirt_size>SM|MD|LG|1X|2X|3X)     # size
              \s*$""", re.X + re.M)
SHIRT_SIZES  = [ (x, x) for x in ['SM','MD','LG','1X','2X','3X'] ]
TUTORIALS_AM = [ ('AM'+x, 'AM'+x) for x in map(str, range(1,8))]
TUTORIALS_PM = [ ('PM'+x, 'PM'+x) for x in map(str, range(1,8))]

months = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
          'Aug', 'Sep', 'Oct', 'Nov', 'Dec']

def date2date(dstr):
    day, month, year = dstr.split('-')
    return datetime.date(int(year, 10), months.index(month), int(day, 10))

class TShirts(object):
    def __init__(self, data):
        self.entries = dict((ent['badge_name'].lower(), ent)
            for ent in ( match.groupdict() for match in shirtre.finditer(data)))
    def get_size(self, name):
        if name.lower() not in self.entries:
            self.entries[name.lower()] = { 'full_name': name, 'badge_name': name,
                                           'missing': True, 'shirt_size': 'LG',
                                           'set': True}
            return 'LG'
        entry = self.entries[name.lower()]
        if 'set' in entry: entry['multiple'] = True
        else: entry['set'] = True
        return entry['shirt_size']
    def get_missing(self):
        return [entry['full_name'] for entry in self.entries.itervalues()
                    if entry.get('missing', False)]
    def get_used_multiple(self):
        return [entry['full_name'] for entry in self.entries.itervalues()
                    if entry.get('multiple', False)]
    def get_not_used(self):
        return [entry['full_name'] for entry in self.entries.itervalues()
                    if not entry.get('set', False)]
    

## validators to add:
##  user == presenter if both set
##  chair matches actual user with chair status
##  keynote only set for speaker of plenary

class Record(models.Model):
    badge_name   = models.CharField(maxlength=100, unique=True)
    last_first   = models.CharField(maxlength=100, blank=True,
                                    help_text="auto-set field")
    letter       = models.CharField(maxlength=1,   blank=True,
                                    help_text="auto-set field")
    card_name    = models.CharField(maxlength=100, blank=True)
    organization = models.CharField(maxlength=100, blank=True,
                                    help_text="currently ignored")
    reg_date     = models.DateField()
    paid         = models.IntegerField(default=0, blank=False, null=False)
    vegan        = models.BooleanField(default=False)
    can_email    = models.BooleanField(default=False)
    public       = models.BooleanField(default=False)
    vendor_only  = models.BooleanField(default=False,
                        help_text="Not an actual registered attendee.")
    
    sync_on_save = models.BooleanField(default=True,
            help_text="If set to true then auto-set and blank smart-set fields"
                      " will be filled in.")
    
    user         = models.ForeignKey(User,      unique=True, null=True, blank=True,
                help_text="smart-set. Needed for some other smart-set fields.")
    presenter    = models.ForeignKey(Presenter, unique=True, null=True, blank=True,
                help_text="smart-set. Needed for some other smart-set fields.")
    
    shirt_size   = models.CharField(maxlength=2, choices=SHIRT_SIZES, default='LG')
    tutorial_am  = models.CharField(maxlength=3, blank=True, choices=TUTORIALS_AM)
    tutorial_pm  = models.CharField(maxlength=3, blank=True, choices=TUTORIALS_PM)
    keynote      = models.BooleanField(default=False,
                        help_text="smart-set field")
    speaker      = models.BooleanField(default=False,
                        help_text="smart-set field")
    vendor       = models.BooleanField(default=False)
    chair        = models.BooleanField(default=False,
                        help_text="smart-set field")
    sponsor      = models.BooleanField(default=False)

    class Admin:
        list_display   = ('badge_name', 'letter', 'shirt_size',
                          'tutorial_am', 'tutorial_pm',
                          'keynote', 'speaker', 'vendor', 'chair', 'sponsor',
                          'vegan', 'can_email', 'public')
        list_filter    = ('letter', 'shirt_size',
                          'tutorial_am', 'tutorial_pm',
                          'keynote', 'speaker', 'vendor', 'chair', 'sponsor',
                          'vegan', 'can_email', 'public', 'vendor_only')
        search_fields  = ('badge_name', 'card_name', 'organization')
        date_hierarchy = 'reg_date'

    class Meta:
        ordering       = ('letter', 'last_first', 'badge_name')

    def __unicode__(self):
        return self.badge_name
    
    @classmethod
    def load_from_file_data(cls, registrations, tshirts):
        tshirts = TShirts(tshirts)
        num = 0
        for idx, match in enumerate(regre.finditer(registrations)):
            entry = match.groupdict()
            try:            
                reg = cls(badge_name = entry['badge_name'], card_name='',
                          tutorial_am='', tutorial_pm='')
                if entry['card_name']: reg.card_name = entry['card_name']
                if entry['tutorial_am']: reg.tutorial_am = entry['tutorial_am']
                if entry['tutorial_pm']: reg.tutorial_pm = entry['tutorial_pm']
                reg.reg_date = date2date(entry['date'])
                reg.shirt_size = tshirts.get_size(entry['badge_name'])
                reg.paid = int(entry['paid'], 10)
                reg.sync_on_save = True
                reg.vegan = bool(entry['vegan'])
                reg.can_email = bool(entry['can_email'])
                reg.public = bool(entry['public'])
                reg.save()
            except Exception, e:
                transaction.rollback_unless_managed()
                print "Exception on line", idx+1, ":"
                print entry
                print unicode(e)
            else:
                num += 1
                transaction.commit_unless_managed()
        return num, tshirts
    
    @classmethod
    def load_from_files(cls, regfn, tsfn):
        num, tshirts = cls.load_from_file_data(
            file(regfn, "U").read(), file(tsfn, "U").read())
        print num, "Registration Entries Loaded"

        missing = tshirts.get_missing()
        if missing: print "MISSING:", len(missing), "\n    ", "\n     ".join(missing)
        not_used = tshirts.get_not_used()
        if not_used: print "NOT USED:", len(not_used), "\n    ", "\n     ".join(not_used)
        multiple = tshirts.get_used_multiple()
        if multiple: print "USED MULTIPLE:", len(multiple), "\n    ", "\n     ".join(multiple)
        
    def save(self):
        self.vendor |= self.vendor_only
        if self.public: self.public = not self.vendor_only
        
        ## last_first autofill
        first, last = '', ''
        if not self.last_first or self.sync_on_save:
            parts = self.badge_name.split()
            first = parts[0]
            if first in HONORIFIC and len(parts)>1: first = parts[1]
            if parts[-1] in SUFFIX:
                last = parts.pop(-2)
            else:
                last = parts.pop()
            parts.insert(0, last+',')
            self.last_first = " ".join(parts)
            
        if self.sync_on_save:
            if not self.user:
                ## add code to try to match up with user
                users = User.objects.filter(first_name__iexact=first,
                                            last_name__iexact=last)
                if users and not Record.objects.filter(presenter=users[0]).count():
                    self.user = users[0]
            if not self.user and not self.presenter:
                ## add code to try to match up with presenter
                pres = Presenter.objects.filter(
                    name__icontains=first).filter(name__icontains=last)
                if pres and not Record.objects.filter(presenter=pres[0]).count():
                    self.presenter = pres[0]
        
        ## letter autofill
        if not self.letter:
            self.letter = self.last_first[0].upper()

        if self.sync_on_save or self.id == None:            
            ## if presenter is set, maybe user should be as well
            if self.presenter and not self.user and self.presenter.user:
                self.user = self.presenter.user
                
            ## if user is set, maybe presenter should be as well
            if self.user and not self.presenter:
                pres = self.user.presenter_set.all()
                if pres: self.presenter = pres[0]
                
            ## set speaker flag
            if not self.speaker:
                if self.presenter:
                    self.speaker = True
                elif self.user:
                    if self.user.proposals_submitted_set.filter(status='A').count():
                        self.speaker = True
                    elif self.user.proposals_coauthored_set.filter(status='A').count():
                        self.speaker = True
                    
            ## session chair check
            if (not self.chair and self.user and
                self.user.groups.filter(name='Session Chairs').count()):
                self.chair=True
            ## keynote check
            if (not self.keynote and self.presenter and
                self.presenter.event_set.filter(type='P', _title__contains='Keynote').count()):
                self.keynote=True

        self.sync_on_save = False
        return super(Record, self).save()
