from django.contrib.auth.models import User, Group
from django.contrib.auth import authenticate
from django.contrib.sites.models import Site
from django.template import RequestContext, Context, loader
from django.core import validators
from django import newforms
from pycon.tags.forms import MultiSelectComboMultiWordTagField
from pycon.restructuredtext.forms import ReSTField
from pycon.core import safe_utf8_encode
from itertools import chain
from models import *
from changelog import log_proposal, log_action, CHANGE
import types

EXCLUDE_PROPOSAL_FIELDS = ['submitter',
                           'attachedfile',
                           'coauthors',
                           'published',
                           'status',
                           'submitted',
                           'updated',
                           'reviewers',
                           'published_description']

DEFAULT_PROPOSAL_TAGS = ['advocacy',
                         'agile',
                         'animation',
                         'ast',
                         'biology',
                         'case study',
                         'community',
                         'concurrency',
                         'core',
                         'coroutines',
                         'databases',
                         'django',
                         'documentation',
                         'dotnet',
                         'education',
                         'eggs',
                         'engineering',
                         'extensions',
                         'games',
                         'gui',
                         'handheld',
                         'ide',
                         'implementations',
                         'jython',
                         'k-12',
                         'language',
                         'legal',
                         'library',
                         'network',
                         'packaging',
                         'panel',
                         'parsing',
                         'project',
                         'science',
                         'security',
                         'shell',
                         'technique',
                         'testing',
                         'turbogears',
                         'tutorial',
                         'twisted',
                         'university',
                         'web',
                         'zope']

DEFAULT_PROPOSAL_TAGS = zip(DEFAULT_PROPOSAL_TAGS, DEFAULT_PROPOSAL_TAGS)
DEFAULT_DURATIONS = [(30, '30min'),
                     (45, '45min')]


class DurationSelection(newforms.Select):
    def __init__(self, attrs=None, choices=(), empty_label=u"---------"):
        self.empty_label = empty_label
        super(DurationSelection, self).__init__(attrs, choices)

    def render(self, name, value, attrs=None, choices=(), **kwdargs):
        empty_choice = ()
        # kwdargs is needed because it is the only way to determine if an
        # override is provided or not.
        if 'empty_label' in kwdargs:
            if kwdargs['empty_label'] is not None:
                empty_choice = ((u'', kwdargs['empty_label']),)
        elif self.empty_label is not None:
            empty_choice = ((u'', self.empty_label),)
        base_choices = self.choices
        self.choices = chain(empty_choice, base_choices)
        if (value is not None and
            value not in dict(chain(empty_choice, base_choices, choices))):
            choices = chain(choices, [(value, unicode(value) + u'min')])
        result = super(DurationSelection, self).render(name, value,
                                                       attrs, choices)
        self.choices = base_choices
        return result

def filter_proposal_fields(f):
    if f.name in EXCLUDE_PROPOSAL_FIELDS:
        return None
    if f.name == 'title':
        ff = f.formfield()
        ff.widget.attrs['size'] = u'85'
        return ff
    if f.name == 'categories':
        ff = f.formfield()
        return MultiSelectComboMultiWordTagField(
            Proposal, choices=DEFAULT_PROPOSAL_TAGS, required=ff.required,
            select_attrs={'class': u"vSelectMultipleField", 'size': u'5' },
            text_attrs={'size': u'55'}, max_tags=10,
            max_length=ff.max_length, min_length=ff.min_length)
    if f.name == 'summary':
        ff = f.formfield()
        ff.widget.attrs['cols'] = 85
        return ff
    if f.name == 'description':
        return ReSTField(include_error=True, require_title=False,
                         rows=30, cols=85, aclass="monospace")
    if f.name == 'duration':
        return newforms.ChoiceField(choices=DEFAULT_DURATIONS,
                                    widget=DurationSelection)
    return f.formfield()

_ProposalForm = newforms.form_for_model(
    Proposal, formfield_callback=filter_proposal_fields)

class ProposalForm(_ProposalForm):
    def __init__(self, request, proposal=None, post=None, *args, **kwdargs):
        self.proposal = proposal
        self.user = request.user
        self.request = request
        if post is None and request.POST:
            post = request.POST
        initial = {}
        if proposal is not None:
            initial.update(vars(proposal))
            initial['categories'] = initial['_categories_cache']
            del initial['_categories_cache'] ## tag field is messed up.
        if 'initial' in kwdargs:
            initial.update(kwdargs['initial'])
        kwdargs['initial'] = initial
        super(ProposalForm, self).__init__(post, *args, **kwdargs)
    def clean_title(self):
        value = self.cleaned_data['title']
        if self.proposal is not None:
            if value == self.proposal.title: return value
        try: Proposal.objects.get(title__exact=value)
        except Proposal.DoesNotExist: return value
        raise newforms.ValidationError('There is already a ' +
            Proposal._meta.verbose_name + ' with a title of "' + value +'".')
    def clean_duration(self):
        value = self.cleaned_data['duration']
        try:
            value = int(value)
        except e:
            raise newforms.ValidationError('Not a valid duration: '  + str(e))
        return value
    def save(self):
        if self.proposal is not None:
            log_change = log_proposal(self.user, self.cleaned_data,
                                      self.proposal)
            res = newforms.save_instance(self, self.proposal,
                                         fail_message='edit')
            log_change()
            return res
        ## new proposal
        prop = super(ProposalForm, self).save(commit=False)
        prop.submitter = self.user
        ### get some new reviewers!!!
        reviewers = []
        try:
            reviewers = Group.objects.get(
                name='Reviewers').user_set.exclude(
                    pk=prop.submitter_id).order_by('?')[:3]
            prop.save()
            prop.reviewers = reviewers
            ## moved to model save
            ##user.groups.add(Group.objects.get(name='Submitters'))
        except:
            if settings.DEBUG: raise
        prop.save()
        log_proposal(self.user, prop)
        emails = []
        t = loader.get_template("propmgr/email/submit_proposal.txt")
        c = {
            'mailto': prop.submitter,
            'email': prop.submitter.email,
            'proposal': prop
        }
        message = t.render(RequestContext(self.request, c))
        emails.append({
            'subject': settings.CONFERENCE_NAME + ' Talk Proposal Submission',
            'body': message,
            'from_email': settings.PROPMGR_FROM_EMAIL,
            'replyto_email': settings.PROPMGR_REPLYTO_EMAIL,
            'to': [prop.submitter.email]})
        t = loader.get_template("propmgr/email/review_proposal.txt")
        for reviewer in reviewers:
            c = {
                'mailto': reviewer,
                'email': reviewer.email,
                'proposal': prop
            }
            message = t.render(RequestContext(self.request, c))
            emails.append({
                'subject': settings.CONFERENCE_NAME +
                           ' New Talk Proposal Submission for Review',
                'body': message,
                'from_email': settings.PROPMGR_FROM_EMAIL,
                'replyto_email': settings.PROPMGR_REPLYTO_EMAIL,
                'to': [reviewer.email]})
        from pycon.core import mail
        #print emails # because dev does not have sendmail enabled.
        mail.send_mass_mail(emails)
        return prop

class NullSelection(newforms.Select):
    def __init__(self, attrs=None, choices=(), empty_label=u"---------"):
        self.empty_label = empty_label
        super(NullSelection, self).__init__(attrs, choices)

    def render(self, name, value, attrs=None, choices=(), **kwdargs):
        empty_choice = ()
        # kwdargs is needed because it is the only way to determine if an
        # override is provided or not.
        if 'empty_label' in kwdargs:
            if kwdargs['empty_label'] is not None:
                empty_choice = ((u'', kwdargs['empty_label']),)
        elif self.empty_label is not None:
            empty_choice = ((u'', self.empty_label),)
        base_choices = self.choices
        self.choices = chain(empty_choice, base_choices)
        result = super(NullSelection, self).render(name, value,
                                                   attrs, choices)
        self.choices = base_choices
        return result

class StatusEntryForm(newforms.Form):
    status = newforms.ChoiceField(required=False, choices=(),
                                  widget=NullSelection)
    def __init__(self, proposal, user, editable=None, *args, **kwdargs):
        if editable is None:
            editable = (not proposal.published and
                        proposal.status in ['A', 'D', 'R'])
        self.editable = editable
        self.proposal = proposal
        self.user = user
        super(StatusEntryForm, self).__init__(*args, **kwdargs)
        self.fields['status'].choices=tuple((key, val)
            for key, val in PROPOSAL_STATUS_CHOICES if key != proposal.status)
        proposal.form = self
    def save(self):
        if not self.editable: return False
        new_status = self['status'].data
        if not new_status or new_status == self.proposal.status:
            return False
        self.proposal.status = new_status
        log_action(self.user, self.proposal, CHANGE, '* Status Change\n')
        self.proposal.save()
        return True
    def __unicode__(self):
        if self.editable:
            return unicode(self['status'])
        if self.proposal.published: return u'Published'
        return u'Not Editable'

def proposal_status_form_filter(query, user, data=None):
    return [ StatusEntryForm(p, user, prefix=u'%d'%p.id, data=data).proposal
             for p in query ]
