#Django core bits
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext, loader, Context
from django.contrib.auth.decorators import login_required, permission_required
from django.db.models import Q
from django.contrib.sites.models import Site
from django.conf import settings
from django.http import HttpResponseRedirect, Http404, HttpResponse
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User, Group
from django.core.mail import EmailMessage
from django.contrib import messages
from datetime import datetime,date,time
import math

#Models
from models import *
from modules.members.models import *

from forms import *

#Others
import random,csv,string

from modules.core.functions import *
from modules.core.decorators import *
from functions import *

from signals import *

def can_register(member,election):

    if not member.approved or member.member_status != 'current' or member.user_type != 'member':
        return False

    allowed = True

    if election.member_types_allowed_register.all():
        if not member.member_type in election.member_types_allowed_register.all():
            allowed = False

    if election.member_groups_allowed_register.all():
        in_groups = False
        for group in election.member_groups_allowed_register.all():
            if member in group.members.all():
                in_groups = True

        if not in_groups:
            allowed = False

    return allowed

def can_vote(member,election):

    allowed = True

    if not member.approved or member.member_status != 'current' or member.user_type != 'member':
        return False

    today = date.today()
    if member.expiry_date and member.expiry_date <= today:
        return False

    if election.member_types_allowed_vote.all():
        if not member.member_type in election.member_types_allowed_vote.all():
            allowed = False

    if election.member_groups_allowed_vote.all():
        in_groups = False
        for group in election.member_groups_allowed_vote.all():
            if member in group.members.all():
                in_groups = True

        if not in_groups:
            allowed = False

    return allowed

def can_view(member,election):

    if not member.approved or member.member_status != 'current' or member.user_type != 'member':
        return False

    allowed = True

    if election.member_types_allowed_view.all():
        if not member.member_type in election.member_types_allowed_view.all():
            allowed = False

    if election.member_groups_allowed_view.all():
        in_groups = False
        for group in election.member_groups_allowed_view.all():
            if member in group.members.all():
                in_groups = True

        if not in_groups:
            allowed = False

    return allowed

@members_only
def election(request,election_slug):

    election = get_object_or_404(Election,slug=election_slug)

    if not election.enabled:
        return render_to_response('public/elections/election-disabled.html',{'election':election},context_instance=RequestContext(request))

    try:
        member = Member.objects.get(user=request.user)
    except:
        raise Http404

    if not can_view(member,election):
        return render_to_response('public/elections/election-denied.html',{'election':election},context_instance=RequestContext(request))

    if election.get_status() == 'registration':
        return HttpResponseRedirect(reverse('elections_election_registration',args=[election.slug]))
    elif election.get_status() == 'registration-complete':
        return render_to_response('public/elections/election-registration-gathering.html',{'election':election},context_instance=RequestContext(request))
    elif election.get_status() == 'voting':
        return HttpResponseRedirect(reverse('elections_election_vote',args=[election.slug]))
    elif election.get_status() == 'complete':
        return render_to_response('public/elections/election-complete.html',{'election':election},context_instance=RequestContext(request))
    else:
        return render_to_response('public/elections/election-disabled.html',{'election':election},context_instance=RequestContext(request))

@members_only
def election_registration(request,election_slug):

    election = get_object_or_404(Election,slug=election_slug)

    try:
        member = Member.objects.get(user=request.user)
    except:
        raise Http404

    today = date.today()

    if election.get_status() != 'registration':
        return HttpResponseRedirect(reverse('elections_election',args=[election.slug]))

    position_choices = election.get_positions_registration_member(member)

    if election.show_dob:
        election_registration_form = ElectionRegistrationDOBForm(initial={'title':member.title,'given_name':member.given_name,'surname':member.surname,'email':member.user.email,'telephone':member.telephone,'hospital_clinic':member.hospital},position_choices=position_choices)
    else:
        election_registration_form = ElectionRegistrationForm(initial={'title':member.title,'given_name':member.given_name,'surname':member.surname,'email':member.user.email,'telephone':member.telephone,'hospital_clinic':member.hospital},position_choices=position_choices)

    if not can_register(member,election) or not position_choices:
        return render_to_response('public/elections/election-registration-denied.html',{'election':election},context_instance=RequestContext(request))

    #try:
    #    election_candidate = ElectionCandidate.objects.get(election=election,member=member)
    #    member_registered = True
    #except:
    #    member_registered = False

    if request.POST:

        if election.show_dob:
            election_registration_form = ElectionRegistrationForm(request.POST,request.FILES,position_choices=position_choices)
        else:
            election_registration_form = ElectionRegistrationDOBForm(request.POST,request.FILES,position_choices=position_choices)

        if election_registration_form.is_valid():

            errors = False

            try:
                position = election_registration_form.cleaned_data['position']
                election_candidate = ElectionCandidate.objects.get(member=member,position=position)
                messages.warning(request,'You have already registered for this position.')
                errors = True
            except ElectionCandidate.DoesNotExist:
                pass

            if request.POST.get('sponsor_email_1') == request.POST.get('sponsor_email_2'):
                messages.error(request,'Please enter two unique email addresses as referrers.')
                errors = True

            try:
                member = Member.objects.get(user__email=request.POST.get('sponsor_email_1'),approved=True, member_status='current',user_type='member')
            except Member.DoesNotExist:
                messages.error(request,'Please enter the email address of an existing member for your Proposer.')
                errors = True

            try:
                member = Member.objects.get(user__email=request.POST.get('sponsor_email_1'),approved=True, member_status='current',user_type='member')
            except Member.DoesNotExist:
                messages.error(request,'Please enter the email address of an existing member for your Seconder.')
                errors = True


            if not errors:
                #try:

                election_candidate = election_registration_form.save(commit=False)
                election_candidate.member = member
                election_candidate.election = election
                election_candidate.save()

                unique_key = random_string_unique_sponsor()
                election_candidate.sponsor_key_1 = unique_key
                election_candidate.save()

                unique_key = random_string_unique_sponsor()
                election_candidate.sponsor_key_2 = unique_key
                election_candidate.save()

                #send sponsor and admin emails
                election_registered.send(sender=None,request=request,election_candidate=election_candidate)

                return HttpResponseRedirect(reverse('elections_election_registration_complete',args=[election.slug]))

                #except:
                #    if election_candidate:
                #        election_candidate.delete()
                #    messages.error(request,'Sorry, could not save your registration, please try again.')

        else:
            messages.error(request,'Please ensure you have completed all fields and try again.')

    return render_to_response('public/elections/election-registration.html',{'election':election,'election_registration_form':election_registration_form},context_instance=RequestContext(request))

def election_registration_approve(request,election_slug,approval_key):

    election = get_object_or_404(Election,slug=election_slug)

    slot = False
    election_candidate = False

    try:
        election_candidate = ElectionCandidate.objects.get(sponsor_key_1=approval_key)
        slot = 1
    except:
        try:
            election_candidate = ElectionCandidate.objects.get(sponsor_key_2=approval_key)
            slot = 2
        except:
            pass

    if slot and election_candidate:
        now = datetime.now()
        if slot == 1:
            election_candidate.sponsor_time_1 = now
            election_candidate.sponsor_confirmed_1 = True
            election_candidate.save()
        elif slot == 2:
            election_candidate.sponsor_time_2 = now
            election_candidate.sponsor_confirmed_2 = True
            election_candidate.save()

        return render_to_response('public/elections/election-registration-approved.html',{'election':election,'election_candidate':election_candidate},context_instance=RequestContext(request))

    else:
        raise Http404


@members_only
def election_registration_complete(request,election_slug):

    election = get_object_or_404(Election,slug=election_slug)

    return render_to_response('public/elections/election-registration-complete.html',{'election':election},context_instance=RequestContext(request))


@members_only
def election_vote(request,election_slug):

    election = get_object_or_404(Election,slug=election_slug)

    try:
        member = Member.objects.get(user=request.user)
    except:
        raise Http404

    today = date.today()

    if election.get_status() != 'voting':
        return HttpResponseRedirect(reverse('elections_election',args=[election.slug]))

    if not can_vote(member,election):
        return render_to_response('public/elections/election-voting-denied.html',{'election':election},context_instance=RequestContext(request))


    positions = election.get_positions_voting_member(member)
    votable_positions = []
    for position in positions:
        votes = ElectionVote.objects.filter(member=member,candidate__position=position)
        candidates = position.get_approved_candidates()
        if not votes and candidates:
            votable_positions.append(position)


    if request.POST:

        errors = False

        for position in positions:

            if position.num_votes > 1:

                if request.POST.getlist('position_%s' % (position.id)):
                    candidates = request.POST.getlist('position_%s' % (position.id))

                    if len(candidates) <= position.num_votes:
                        election_votes = ElectionVote.objects.filter(member=member,candidate__position=position)
                        if election_votes:
                            errors = True
                            messages.error(request,'Sorry, you have already voted for position %s' % (position.name))


                        for candidate_id in candidates:

                            candidate = ElectionCandidate.objects.get(id=candidate_id,position=position)

                            if not election.vote_for_self:
                                if candidate.member == member:
                                    messages.error(request,'Sorry, you cannot vote for yourself')

                            if not errors:
                                try:
                                    vote = ElectionVote(member=member,candidate=candidate)
                                    vote.save()
                                except:
                                    messages.error(request,'Could not vote for position %s' % (position.name))
                                    errors  = True
                                    pass
                    else:
                        errors = True
                        messages.error(request,'Sorry you have selected too many candidates for the position <strong>%s</strong>' % (position))

            else:
                if request.POST.get('position_%s' % (position.id)):
                    candidate_id = request.POST['position_%s' % (position.id)]

                    candidate = ElectionCandidate.objects.get(id=candidate_id,position=position)

                    if not election.vote_for_self:
                        if candidate.member == member:
                            messages.error(request,'Sorry, you cannot vote for yourself')
                            errors = True

                    election_votes = ElectionVote.objects.filter(member=member,candidate__position=position)
                    if election_votes:
                        errors = True
                        messages.error(request,'Sorry, you have already voted for position %s' % (position.name))

                    if not errors:
                        try:
                            vote = ElectionVote(member=member,candidate=candidate)
                            vote.save()
                        except:
                            messages.error(request,'Could not vote for position %s' % (position.name))
                            errors  = True
                            pass

        if not errors:
            return HttpResponseRedirect(reverse('elections_election_vote_complete',args=[election.slug]))
        else:
            votes = ElectionVote.objects.filter(member=member,candidate__election=election)
            for vote in votes:
                vote.delete()

    return render_to_response('public/elections/election-voting.html',{'election':election,'positions':positions,'votable_positions':votable_positions},context_instance=RequestContext(request))

@members_only
def election_vote_complete(request,election_slug):

    election = get_object_or_404(Election,slug=election_slug)

    return render_to_response('public/elections/election-voting-complete.html',{'election':election},context_instance=RequestContext(request))


@members_only
def election_vote_candidate(request,election_slug,candidate_id):

    election = get_object_or_404(Election,slug=election_slug)
    candidate = get_object_or_404(ElectionCandidate,election=election,id=candidate_id)

    try:
        member = Member.objects.get(user=request.user)
    except:
        raise Http404

    today = date.today()

    if election.get_status() != 'voting':
        return HttpResponseRedirect(reverse('elections_election',args=[election.slug]))

    if not can_vote(member,election):
        return render_to_response('public/elections/election-voting-denied.html',{'election':election},context_instance=RequestContext(request))

    return render_to_response('public/elections/election-candidate.html',{'election':election,'candidate':candidate},context_instance=RequestContext(request))
