"""
Brochures view.
"""
import os, copy
from hashlib import md5
from django.conf import settings
from django.core.urlresolvers import reverse
from django.forms.formsets import formset_factory
from django.shortcuts import (render_to_response, get_object_or_404,
                              HttpResponseRedirect, HttpResponse, Http404)
from django.http import HttpResponseServerError
from django.template import RequestContext, loader, TemplateDoesNotExist
from django.core.mail.message import EmailMessage
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core import serializers


#this should get replaced by a built-in baseviews in Django 1.3.x.
#don't use baseviews in a project unless there is a real need to override
#parts of the view behaviour!
from rlextra.thirdparty.baseviews import FormView, BasicView

from models import Pdf, Section, Subset
from forms import CreateForm, DownloadForm, makeDownloadFormActionField
from utils import (generate_single_pdf_brochure, generate_all_pdf_brochure, 
                   generate_flipbook_js, get_template_choices, get_dependant_sections, 
                   deliver_pdf, get_pdf_md5, get_brochure_template_path, 
                   get_mandatory_sections, getparams_to_string)
from rldjango.common.logging import logview, logviewmethod


@login_required
@logview
def choose(request):
    """
    List all avilable brochures.
    - by pdf
    - by groups
    """
    context = RequestContext(request, {
        "pdfs": Pdf.objects.all() # pylint: disable-msg=E1101
        })
    
    return render_to_response(get_template_choices('choose.html'), context)

ALL_COURSES_SLUG = 'all-courses'

def all_pdfs(request):
    """
    Allow choice of sections/subsections from all PDFs.
    """
    PdfFormSet = formset_factory(CreateForm, extra=0)
    # Forms expect to be passed PDFs
    pdfs_list = [pdf for pdf in Pdf.objects.all().exclude(slug__exact=ALL_COURSES_SLUG)]
    dummy_pdf = Pdf.objects.get(slug__exact=ALL_COURSES_SLUG)
    if request.method == 'POST':
        pdf_formset = PdfFormSet(request.POST, initial=pdfs_list)
        if pdf_formset.is_valid():
            section_ids = []

            cached, pdf_md5 = get_pdf_md5(dummy_pdf.id, request)
            
            if not getattr(settings, 'BINDER_CACHING', False):
                cached = False
            
            pdfs = []
            for sections in pdf_formset.cleaned_data:
                for section_id, value in sections.iteritems():
                    if value:
                        section_ids.append(section_id)
                        section = Section.objects.get(pk=section_id)
                        # Include containing abstract section
                        parent_section = Section.objects.filter(pdf=section.pdf,
                                                                order__lt=section.order,
                                                                abstract=True).order_by('-order')[0]
                        section_ids.append(parent_section.id)
                        # Include all mandatory sections (and sections inside mandatory abstract sections)
                        if section.pdf not in pdfs:
                            mandatory_sections = Section.objects.filter(pdf=section.pdf,
                                                                        mandatory=True)
                            for mandatory_section in mandatory_sections:
                                section_ids.append(mandatory_section.id)
                                if mandatory_section.abstract:
                                    following_sections = Section.objects.filter(pdf=section.pdf,
                                                                                order__gt=mandatory_section.order)
                                    for following_section in following_sections:
                                        if following_section.abstract:
                                            break
                                        section_ids.append(following_section.id)
                            pdfs.append(section.pdf)
            
            if section_ids:
                #remove cover section of first PDF (it is handled separately in the template)
                if pdfs[0].cover:
                    section_ids.remove(pdfs[0].cover.id)
                else:
                    pass # No Cover Section present
                sections = Section.objects.filter(pk__in=section_ids)
                
                extra = request.REQUEST
                pdf_data = generate_all_pdf_brochure(
                    pdf_name=pdf_md5,
                    sections=sections,
                    first_pdf=pdfs[0],
                    extra=extra,
                    )
                pdf_filename = '%s.pdf' % (pdf_md5)
        
                pdf_file = open(os.path.join(settings.OUTPUT_ROOT, pdf_filename), 'wb')
                pdf_file.write(pdf_data)
                pdf_file.close()
                js_data = generate_flipbook_js(
                    sections=sections,
                    pdf_url=os.path.join(settings.OUTPUT_URL, pdf_filename)
                    )
                js_filename = '%s.js' % (pdf_md5)
                js_file = open(os.path.join(settings.OUTPUT_ROOT, js_filename), 'wb')
                js_file.write(js_data)
                js_file.close()
        
                return HttpResponseRedirect(
                    dummy_pdf.get_success_url(pdf_md5)+getparams_to_string(request)
                )
    else:
        pdf_formset = PdfFormSet(initial=pdfs_list)
    context = RequestContext(request, {
        "pdf_formset": pdf_formset
        })
    return render_to_response(get_template_choices('all_courses/all_pdfs.html'), context)

@login_required
@logview
def home(request):
    context = RequestContext(request, {
        "pdfs": [], "subsets": [],
        })
    if request.user.is_superuser:
        context['pdfs'] = Pdf.objects.all()
        context['subsets'] = Subset.objects.all()
    else:
        for group in request.user.groups.all():
            pdfs = Pdf.objects.filter(owner=group)
            for pdf in pdfs:
                context['pdfs'].append(pdf)
                subsets = Subset.objects.filter(pdf=pdf.id)
                for subset in subsets:
                    context['subsets'].append(subset)
    print context['pdfs']
    print context['subsets']
    return render_to_response(get_template_choices('home.html'), context)

class LogFormView(FormView):
    def __call__(self):
        def f(request,pdf_name=None,*args,**kwds):
            return FormView.__call__(self)
        f.func_name = self.__class__.__name__
        return logview(f)(self.request,self.pdf_name,*self.args,**self.kwargs)

class LogBasicView(BasicView):
    def __call__(self):
        def f(request,pdf_name=None,*args,**kwds):
            return BasicView.__call__(self)
        f.func_name = self.__class__.__name__
        return logview(f)(self.request,self.pdf_name,*self.args,**self.kwargs)

class Create(LogFormView):
    """
    Baseview for creating brochures..
    """

    form_class = CreateForm
    
    def __init__(self, request, pdf_name=None, *args, **kwargs):
        """
        Check if pdf name exists..
        """
        self.pdf_name = pdf_name
        self.pdf = get_object_or_404(Pdf, slug=pdf_name)

        super(Create, self).__init__(request, *args, **kwargs)

    def get_template(self):
        """
        Create template choices list for template overriding.
        
        We have a standard template available for new documents,
        but allow it to be overridden if you create another one
        in a subdirectory matching the PDF name.
        
        """
        # pylint: disable-msg=W0212
        template = 'create.html'
        try:
            #if the first line succeeds, there is a document-specific template
            loader.get_template('binder/%s/%s' % (self.pdf_name, template))
            template = '%s/%s' % (self.pdf_name, template)
        except TemplateDoesNotExist:
            pass
        return get_template_choices(template) 

    def get_form(self):
        """
        Get the default form for the view, bound with data if provided.
        """
        if self.request.REQUEST.get('sections', None):
            self.form_options.update({
                'selected_sections': self.request.REQUEST['sections']
                })
        if self.request.REQUEST.get('expanded', None):
            self.form_options.update({
                'expanded_sections': self.request.REQUEST['expanded']
                })
        self.form_options.update({
            'pdf': self.pdf,
            })
        return super(Create, self).get_form()

    def uncached_context(self):
        """
        Provide the context that should not be cached.
        """
        owner_logged_in = False
        if self.pdf.owner in self.request.user.groups.all() or self.request.user.is_superuser:
            owner_logged_in = True
        
        context = super(Create, self).uncached_context()
        context.update({'pdf': self.pdf, 'owner_logged_in' : owner_logged_in})
        return context

    def process_form(self):
        """
        The method to process POST requests. Return an HttpResponse when you
        need to circumvent normal view processing, such as redirecting to a
        success url.
        """
        if self.form.is_valid():
            KV = getattr(self.request,'_logging_dict',{})
            section_ids = []
            missing_section_ids = []
            slugs=[]
            
            for section_id, value in self.form.cleaned_data.iteritems():
                if value:
                    section_ids.append(section_id)
                else:
                    missing_section_ids.append(section_id)

            if section_ids:
                sections = Section.objects.filter(pk__in=section_ids)
                
                #gather a list of section SLUGS for use in log
                for section in sections:
                    slugs.append(section.slug)

                self.request._logging_dict['selected_sections'] = ','.join(slugs)

                cached, pdf_md5 = get_pdf_md5(self.pdf.id, self.request)
                KV['pdf_was_cached'] = cached
                KV['pdf_md5'] = pdf_md5
                
                if not getattr(settings, 'BINDER_CACHING', False):
                    cached = False
                
                extra = self.request.REQUEST

                if not cached:
                    pdf_data = generate_single_pdf_brochure(
                        pdf_name=self.pdf_name,
                        sections=sections,
                        missing_sections=missing_section_ids,
                        extra=extra,
                        )
                    pdf_filename = pdf_md5 + '.pdf'

                    pdf_file = open(self.pdf.get_pdf_path(pdf_md5), 'wb')
                    pdf_file.write(pdf_data)
                    pdf_file.close()
                    js_data = generate_flipbook_js(
                        sections=sections,
                        pdf_url=os.path.join(settings.OUTPUT_URL, pdf_filename)
                        )
                    js_filename = pdf_md5 + '.js'
                    js_file = open(os.path.join(settings.OUTPUT_ROOT, js_filename), 'wb')
                    js_file.write(js_data)
                    js_file.close()

                return HttpResponseRedirect(
                    self.pdf.get_success_url(pdf_md5)+getparams_to_string(self.request)
                    )


class Download(LogFormView):
    """
    Baseview for download brochures.
    """
    form_class = DownloadForm
    
    def __init__(self, request, pdf_name=None, *args, **kwargs):
        """
        Check if pdf name exists..
        """
        self.pdf_name = pdf_name
        self.pdf = get_object_or_404(Pdf, slug=pdf_name)
        #don't do this here.
        #self.form_class = DownloadForm(request.POST, request=request)

        super(Download, self).__init__(request, *args, **kwargs)

    def get_form(self):
        """
        Get the default form for the view, bound with data if provided.
        """
        data = getattr(self.request, 'POST', None)
        if data:
            self.form_options.update({'data': data})
        
        files = getattr(self.request, 'FILES', None)
        if files:
            self.form_options.update({'files': files})
        
        download_display = self.request.GET.get('download_display','123')
        download_option = self.request.GET.get('download_option','download')
        C = self.form_class
        if download_display or download_option:
            class DownloadForm(C):
                action = makeDownloadFormActionField(download_display,download_option)
            C = DownloadForm
        return C(**self.form_options)
    
    def get_template(self):
        """
        Create template choices list for template overriding.
        """
        template = 'download.html'
        try:
            #if the first line succeeds, there is a document-specific template
            loader.get_template('binder/%s/%s' % (self.pdf_name, template))
            template = '%s/%s' % (self.pdf_name, template)
        except TemplateDoesNotExist:
            pass
        return get_template_choices(template) 

    def uncached_context(self):
        """
        Provide the context that should not be cached.
        """
        if os.path.exists(self.pdf.get_source_pdf_path()) and os.path.exists(self.pdf.get_pdf_path(self.kwargs['pdf_md5'])):
            context = super(Download, self).uncached_context()
            context.update({
                'pdf_filename': self.kwargs['pdf_md5'] + '.pdf',
                'js_filename': self.kwargs['pdf_md5'] + '.js',            
                'recaptcha': settings.RECAPTCHA_PUBLIC_KEY,
                'pdf_size': '%.2f KB' % (os.path.getsize(self.pdf.get_pdf_path(self.kwargs['pdf_md5'])) / 1024.0)
                })

            return context
        else:
            raise Http404('No PDF matches the given query.')

    def process_form(self):
        """
        Returns response.
        """
        if self.form.is_valid():
            return deliver_pdf(self.pdf.get_pdf_path(self.kwargs['pdf_md5']),
                        self.pdf.slug,
                        self.form.cleaned_data['action'],
                        email_to=self.form.cleaned_data['email_to'],
                        body=self.form.cleaned_data['email_body'],
                        email_from=self.form.cleaned_data.get('email_from', None),
                        request=self.request,
                        output_filename=self.pdf_name
                    )
            
    def get_download_url(self): # pylint: disable-msg=W0221
        """
        Returns download url.
        """
        return reverse('binder_download', args=[self.pdf_name, self.kwargs['pdf_md5']])

    def get_flipbook_url(self): # pylint: disable-msg=W0221
        """
        Returns flipbook url.
        """
        return reverse('binder_flipbook', args=[self.pdf_name, self.kwargs['pdf_md5']])


class FlipBook(LogBasicView):
    """
    Shows flip book.
    """
    
    def __init__(self, request, pdf_name=None, *args, **kwargs):
        """
        Check if pdf name exists..
        """
        self.pdf_name = pdf_name

        super(FlipBook, self).__init__(request, *args, **kwargs)

    def get_template(self):
        """
        Create template choices list for template overriding.
        """
        template = 'flip_book.html'
        try:
            #if the first line succeeds, there is a document-specific template
            loader.get_template('binder/%s/%s' % (self.pdf_name, template))
            template = '%s/%s' % (self.pdf_name, template)
        except TemplateDoesNotExist:
            pass
        return get_template_choices(template) 


    def uncached_context(self):
        """
        Provide the context that should not be cached.
        """
        return {
            'js_file': os.path.join(settings.OUTPUT_URL, '%s.js' % self.kwargs['pdf_md5'])
            }

@logview
def api(request, pdf_name):
    pdf = Pdf.objects.get(slug=pdf_name)
    
    sections = []
    if 'subset' in request.REQUEST:
        for section in Subset.objects.get(slug=request.REQUEST['subset']).sections.all():
            sections.append(str(section.id))
    sections = ','.join(sections)
    if 'sections' in request.REQUEST:
        sections += ',%s' % request.REQUEST['sections']
        
    sections = get_mandatory_sections(pdf.id,sections)
    sections = get_dependant_sections(sections, pdf_id=pdf.id)
    if not len(sections):
        return HttpResponseServerError('At least one section is required')
        
    cached, pdf_md5 = get_pdf_md5(pdf.id, request)
    pdf_filename = pdf_md5 + '.pdf'
        
    slugs=[]
    KV = getattr(request,'_logging_dict',{})
    for section in sections:
        slugs.append(section.slug)
    request._logging_dict['selected_sections'] = ','.join(slugs)
    KV['pdf_was_cached'] = cached
    KV['pdf_md5'] = pdf_md5
    
    email_to=''
    disposition=''
    subject=''
    body=''
    email_from=''
    
    if 'disposition' in request.REQUEST:
        disposition=request.REQUEST['disposition']
    else:
        disposition='inline'
    if disposition=='email':
        if 'email_to' in request.REQUEST:
            email_to=request.REQUEST['email_to'].split(',')[:5] #maximum 5 email addresses
        else:
            return HttpResponseServerError("You need to supply an 'email_to' parameter.")
        if 'subject' in request.REQUEST:
            subject=request.REQUEST['subject']
        if 'body' in request.REQUEST:
            body=request.REQUEST['body']
        if 'email_from' in request.REQUEST:
            email_from=request.REQUEST['email_from']
    

    extra = request.REQUEST

    if not cached:
        pdf_data = generate_single_pdf_brochure(
                            pdf_name=pdf_name,
                            sections=sections,
                            extra=extra,
                            )
        pdf_file = open(pdf.get_pdf_path(pdf_md5), 'wb')
        pdf_file.write(pdf_data)
        pdf_file.close()
        js_data = generate_flipbook_js(
                        sections=sections,
                        pdf_url=os.path.join(settings.OUTPUT_URL, pdf_filename)
                        )
        js_filename = pdf_md5 + '.js'
        js_file = open(os.path.join(settings.OUTPUT_ROOT, js_filename), 'wb')
        js_file.write(js_data)
        js_file.close()
        
    return deliver_pdf(pdf.get_pdf_path(pdf_md5), pdf_name, disposition, email_to, subject, body, email_from, api=True)

@logview
def sections(request, pdf_name):
    sections=Section.objects.filter(pdf__slug=pdf_name)

    json_serializer = serializers.get_serializer("json")()
    
    return HttpResponse(json_serializer.serialize(sections, ensure_ascii=False, indent=2), mimetype='text/plain; charset=utf-8')

@logview
def help(request, pdf_name):
    pdf=Pdf.objects.get(slug=pdf_name)
    sections=Section.objects.filter(pdf__slug=pdf_name)
    subsets=Subset.objects.filter(pdf__id=pdf.id)
    
    t=get_brochure_template_path(pdf_name,'help.html')
    return render_to_response(t, {'pdf':pdf, 'sections':sections,'subsets':subsets})

def notes(request):
    
    return render_to_response("binder/notes.html",)
