"""
Brochure models..
"""
import glob
import json
import os
import datetime
from hashlib import md5
from django.conf import settings
from django.db import models
from rlextra.utils.pdf2jpeg import pdfConvert

from reportlab.pdfgen import canvas
from rlextra.pageCatcher.pageCatcher import storeForms
from rlextra.pageCatcher.pdfexplorer import PdfExplorer

from django.contrib.auth.models import Group
from django.core.urlresolvers import reverse

class Pdf(models.Model):
    """
    This holds data about raw PDFs.
    """
    
    name = models.CharField(max_length=200)
    slug = models.SlugField(max_length=100)
    owner = models.ForeignKey(Group, blank=True, null=True, help_text="This is a Django auth group and users need to be added to this group.")
    source = models.FileField(upload_to=settings.UPLOAD_DIR)
    width = models.FloatField(blank=True, null=True)
    height = models.FloatField(blank=True, null=True)
    cover = models.ForeignKey(
        'Section', blank=True, null=True,
        related_name='cover_section', help_text="This section will be included first.")
    caption = models.CharField(max_length=200, blank=True, null=True, help_text="Displayed at top of search pages.")
    footnote = models.CharField(max_length=200, blank=True, null=True, help_text="Displayed at bottom of search pages.")
    page_rotations = models.CharField(max_length=200, blank=True, null=True, help_text="Allows you to specify which pages need rotating. e.g '2l,5r,6l' means pages 2 and 6 need to be rotated to the left before including in output PDF, page 5 needs to be rotated to the right.")
    
    modified = models.DateTimeField(blank=True, null=True)
    data_updated = models.DateTimeField(blank=True, null=True)
    images_updated = models.DateTimeField(blank=True, null=True)
    order = models.PositiveSmallIntegerField(default=0)
    interface_text_color = models.CharField(max_length=7, blank=True, null=True)
    
    class Meta:
        ordering = ('order', 'id')

    def __unicode__(self):
        return self.name

    def save(self, *args, **kwargs): # pylint: disable-msg=W0221
        """
        Add width and height if not set manually.
        """

        if not self.width or not self.height:
            pdf = self.get_pdf()
            try:
                box = pdf.getCropBox(0)
            except KeyError:
                box = pdf.getPageSize(0)

            width, height = [box[2] - box[0], box[3] - box[1]]

            self.width = width
            self.height = height
            
        super(Pdf, self).save(*args, **kwargs)

    def delete(self, *args, **kwargs): # pylint: disable-msg=W0221
        """
        Delete generated js file for section.
        """
        try:
            os.remove(self.get_info_file_path)
        except:
            pass

        super(Pdf, self).delete(*args, **kwargs)

    def get_info_filename(self):
        """
        Returns filename of js file with adtional pdf informations. 
        """
        return '%s.js' % self.slug

    def get_info_file_path(self):
        """
        Returns path to js file with adtional pdf informations. 
        """
        return os.path.join(settings.SECTIONS_ROOT, self.get_info_filename())

    def get_info_file_url(self):
        """
        Returns url to js file with adtional pdf informations. 
        """
        return os.path.join(settings.SECTIONS_URL, self.get_info_filename())
    
    def get_pdf(self):
        """
        Return PdfExplorer instance
        """
        if not getattr(self, '_pdf', None):
            self._pdf = PdfExplorer(self.source.file.read())
        return self._pdf
    
    def get_pdf_filename(self):
        """
        Returns filename of section pdf file.
        """
        return self.source.file.name
        
    def get_source_pdf_path(self):
        """
        Returns full path to source pdf file.
        """
        return os.path.join(settings.UPLOAD_ROOT, self.get_pdf_filename())
        
    def get_pdf_path(self, pdf_md5=''):
        """
        Returns full path to output pdf.
        """
        return os.path.join(settings.OUTPUT_ROOT, pdf_md5 + '.pdf')
        
    def get_pdf_data_path(self):
        """
        Returns full path to pageCatcher data storage path.
        """
        return os.path.join(settings.SECTIONS_ROOT, '%s.data'%self.slug)
    
    def generate_pdf_data(self):
        """
        Generates the JS info file and the parsed PDF data file.
        """
        print "generate_pdf_data: pdf=%s" % (self.slug)
        js_data = {}
        
        for section in self.section_set.all(): # pylint: disable-msg=E1101
            js_data['id_section_%s' % section.id] = section.get_page_count()
            
        js_file = open(self.get_info_file_path(), 'w')
        js_file.write('var pdf_info = ' + json.dumps(js_data))
        js_file.close()
        
        storeForms(self.get_source_pdf_path(), self.get_pdf_data_path(), all=True, prefix=self.slug)
        
        self.data_updated = datetime.datetime.now() + datetime.timedelta(seconds=1) 
        self.save()
        
    def generate_images(self):
        """
        Generate thumbnails, small and large
        """
        print "generate_images: pdf=%s" % (self.slug)

        img_dir = settings.SECTIONS_ROOT

        for filename in glob.glob(os.path.join(img_dir, '%s_*.jpg' % self.slug)):
            os.remove(filename)

        for dir_name, scale in settings.FLIPBOOK_IMAGES:
            if dir_name == 'normal':
                out_dir = img_dir
            else:
                out_dir = os.path.join(img_dir, dir_name)

            if not os.path.exists(out_dir):
                os.makedirs(out_dir)
            print "    pdfConvert: dir_name=%s" % (dir_name)
            pdfConvert(
                self.source.file.name,
                useCropBox=True,
                outfile="%s_page" % self.slug + '%',
                outDir=out_dir,
                scale=scale,
                )

        self.images_updated = datetime.datetime.now() + datetime.timedelta(seconds=1) 
        self.save()
        
    def check_data(self):
        """
        Check if data files are in sync.
        """
        if self.source:
            if not self.modified:
                self.modified = datetime.datetime.now()
                self.save()
            if not self.data_updated or self.modified > self.data_updated:
                return False
        return True
    
    def check_images(self):
        """
        Check if generated images are in sync.
        """
        if self.source:
            if not self.modified:
                self.modified = datetime.datetime.now()
                self.save()
            if not self.images_updated or self.modified > self.images_updated:
                return False
        return True
    
    
    def rotation_types(self):
        """
        Are there left, right or both types of rotation?
        """
        left = 0
        right = 0
        if self.page_rotations:
            for rotation in self.page_rotations.split(','):
                if 'l' in rotation:
                    left += 1
                elif 'r' in rotation:
                    right += 1
            if left and right:
                return 'both'
            elif left:
                return 'l'
            elif right:
                return 'r'
        return False
    
    def page_rotation(self, page_num):
        """
        Is a particular page rotated? return 'l' or 'r' for left or right.
        """
        if self.page_rotations and str(page_num) in self.page_rotations:
            for rotation in self.page_rotations.split(','):
                rotation = rotation.strip()
                if str(page_num) == rotation[:-1]:
                    return rotation[-1:]
        return False
    
    def get_success_url(self, pdf_filename): # pylint: disable-msg=W0221
        """
        Returns download url.
        """
        return reverse('binder_download', args=[self.slug, pdf_filename])
    

class Section(models.Model):
    """
    This defines sections generated from pdf.
    """
    name = models.CharField(max_length=200)
    pdf = models.ForeignKey(Pdf, blank=True, null=True)
    slug = models.SlugField(max_length=100)
    abstract = models.BooleanField(default=False)
    hidden = models.BooleanField(default=False)
    mandatory = models.BooleanField(default=False)
    start = models.PositiveSmallIntegerField(default=0)
    end = models.PositiveSmallIntegerField(default=0)
    source = models.FileField(upload_to=settings.SECTIONS_DIR, blank=True, null=True)
    order = models.PositiveSmallIntegerField(default=0)
    updated = models.DateTimeField(blank=True, null=True)
    extra = models.CharField(max_length=1000, blank=True, null=True)
    
    class Meta: # pylint: disable-msg=W0232,C0111,R0903
        ordering = ('pdf__order', 'order', 'id')


    def __unicode__(self):
        #return self.name
        return '%s - %s' % (self.pdf, self.name)

    def delete(self, *args, **kwargs): # pylint: disable-msg=W0221
        """
        Delete generated pages for page flip.
        """
        img_dir = os.path.dirname(self.source.file.name)
        patern = 'section_%s*jpg' % self.id # pylint: disable-msg=E1101

        super(Section, self).delete(*args, **kwargs)

        for item in settings.FLIPBOOK_IMAGES:
            if item[0] == 'normal':
                files = glob.glob(os.path.join(img_dir, patern))
            else:
                files = glob.glob(os.path.join(img_dir, item[0], patern))

            for _file in files:
                os.remove(_file)

    def save(self, *args, **kwargs): # pylint: disable-msg=W0221
        """
        Add order.
        """
        
        S=Section.objects.filter(pdf=self.pdf,slug=self.slug)
        
        if len(S) > 1 or (len(S) == 1 and S[0].id != self.id):
            raise ValueError('Non-unique slug %r' % self.slug)
        
        if not getattr(self, 'id') and not self.order:
            try:
                self.order = self.pdf.section_set.latest('pk').order + 1
            except Section.DoesNotExist: # pylint: disable-msg=E1101
                self.order = 1

        self.updated = datetime.datetime.now()
        super(Section, self).save(*args, **kwargs)

    def get_page_count(self):
        """
        Returns actual page count for this section.
        """

        return self.end - self.start + 1
        
    def get_pdf_page_numbers(self):
        """
        Returns document source pages numbers that are used in the section - zero first.
        """
        page_numbers = []
        for i in range(self.start-1, self.end):
            if i >= 0:
                page_numbers.append(i)
        return page_numbers
    
    def get_flipbook_pages(self):
        """
        Returns path to page images used in flip book.
        """
        images = []
        for i in self.get_pdf_page_numbers():
            images.append('%s_page_%04d.jpg' % (self.pdf.slug, i+1))
        return images
        
    
class Subset(models.Model):
    """
    Provides an optional way of grouping multiple sections together under one slug name
    """
    pdf = models.ForeignKey(Pdf)
    name = models.CharField(max_length=200)
    slug = models.SlugField(max_length=100)
    sections = models.ManyToManyField(Section, blank=True, null=True)
    modified = models.DateTimeField(blank=True, null=True)
    
    def save(self, *args, **kwargs):
        self.modified = datetime.datetime.now()
        super(Subset, self).save(*args, **kwargs)
    
    def get_section_count(self):
        return len(self.sections.all())
    get_section_count.short_description = 'Section count'
    
    def get_page_count(self):
        num = 0
        for section in self.sections.all():
            num += section.get_page_count()
        return num
    get_page_count.short_description = 'Page count'

