## $Id: fileviewlets.py 12062 2014-11-26 13:03:56Z henrik $
##
## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##

import os
import grok
from zope.component import getUtility
from zope.interface import Interface
from waeup.ikoba.interfaces import (
    IIkobaObject, IExtFileStore, IFileStoreNameChooser)
from waeup.ikoba.interfaces import MessageFactory as _
from waeup.ikoba.browser import DEFAULT_IMAGE_PATH
from waeup.ikoba.utils.helpers import (
    string_from_bytes, file_size, get_fileformat)
from waeup.ikoba.browser.layout import (
    default_filedisplay_template,
    default_fileupload_template)

from waeup.ikoba.customers.interfaces import (
    ICustomer, ICustomersUtils)

from waeup.ikoba.customers.documents import (
    CustomerSampleDocument, CustomerPDFDocument
    )
from waeup.ikoba.customers.browser import (
    CustomerBaseDisplayFormPage, CustomerBaseManageFormPage,
    CustomerFilesUploadPage,
    DocumentDisplayFormPage, DocumentManageFormPage, DocumentEditFormPage,
    PDFDocumentSlipPage,)

grok.context(IIkobaObject)  # Make IIkobaObject the default context
grok.templatedir('browser_templates')

ALLOWED_FILE_EXTENSIONS = ('jpg', 'png', 'pdf', 'tif', 'fpm')


def handle_file_delete(context, view, download_name):
    """Handle deletion of customer file.

    """
    store = getUtility(IExtFileStore)
    store.deleteFileByContext(context, attr=download_name)
    context.writeLogMessage(view, 'deleted: %s' % download_name)
    view.flash(_('${a} deleted.', mapping={'a': download_name}))
    return


def handle_file_upload(upload, context, view, max_size, download_name=None):
    """Handle upload of customer file.

    Returns `True` in case of success or `False`.

    Please note that file pointer passed in (`upload`) most probably
    points to end of file when leaving this function.
    """
    # Check some file requirements first
    size = file_size(upload)
    if size > max_size:
        view.flash(_('Uploaded file is too big.'), type="danger")
        return False
    upload.seek(0)  # file pointer moved when determining size
    dummy,ext = os.path.splitext(upload.filename)
    # fpm files are expected to be fingerprint minutiae, file
    # format is not yet checked
    if ext == '.fpm':
        file_format = 'fpm'
    else:
        file_format = get_fileformat(None, upload.read(512))
        upload.seek(0)  # same here
    if file_format is None:
        view.flash(_('Could not determine file type.'), type="danger")
        return False
    basename, expected_ext = os.path.splitext(download_name)
    if expected_ext:
        if '.' + file_format != expected_ext:
            view.flash(_('${a} file extension expected.',
                mapping={'a': expected_ext[1:]}), type="danger")
            return False
    else:
        if not file_format in ALLOWED_FILE_EXTENSIONS:
            view.flash(
                _('Only the following extensions are allowed: ${a}',
                mapping={'a': ', '.join(ALLOWED_FILE_EXTENSIONS)}),
                type="danger")
            return False
        download_name += '.' + file_format
    store = getUtility(IExtFileStore)
    file_id = IFileStoreNameChooser(context).chooseName(attr=download_name)
    store.createFile(file_id, upload)
    context.writeLogMessage(view, 'uploaded: %s (%s)' % (
        download_name,upload.filename))
    view.flash(_('File ${a} uploaded.', mapping={'a': download_name}))
    return True


class FileManager(grok.ViewletManager):
    """Viewlet manager for uploading files, preferably scanned images.
    """
    grok.name('files')


class FileDisplay(grok.Viewlet):
    """Base file display viewlet.
    """
    grok.baseclass()
    grok.viewletmanager(FileManager)
    template = default_filedisplay_template
    grok.order(1)
    label = _(u'File')
    title = _(u'Scan')
    download_name = u'filename.jpg'

    @property
    def file_exists(self):
        image = getUtility(IExtFileStore).getFileByContext(
            self.context, attr=self.download_name)
        if image:
            return True
        else:
            return False


class FileUpload(FileDisplay):
    """Base upload viewlet.
    """
    grok.baseclass()
    grok.viewletmanager(FileManager)
    template = default_fileupload_template
    tab_redirect = '#tab2-top'
    mus = 1024 * 150
    upload_button =_('Upload selected file')
    delete_button = _('Delete')

    @property
    def show_viewlet(self):
        customers_utils = getUtility(ICustomersUtils)
        if self.__name__ in customers_utils.SKIP_UPLOAD_VIEWLETS:
            return False
        return True

    @property
    def input_name(self):
        return "%s" % self.__name__

    def update(self):
        self.max_upload_size = string_from_bytes(self.mus)
        delete_button = self.request.form.get(
            'delete_%s' % self.input_name, None)
        upload_button = self.request.form.get(
            'upload_%s' % self.input_name, None)
        if delete_button:
            handle_file_delete(
                context=self.context, view=self.view,
                download_name=self.download_name)
            self.view.redirect(
                self.view.url(
                    self.context, self.view.__name__) + self.tab_redirect)
            return
        if upload_button:
            upload = self.request.form.get(self.input_name, None)
            if upload:
                # We got a fresh upload
                handle_file_upload(upload,
                    self.context, self.view, self.mus, self.download_name)
                self.view.redirect(
                    self.view.url(
                        self.context, self.view.__name__) + self.tab_redirect)
            else:
                self.view.flash(_('No local file selected.'), type="danger")
                self.view.redirect(
                    self.view.url(
                        self.context, self.view.__name__) + self.tab_redirect)
        return


class Image(grok.View):
    """Renders images for customers.
    """
    grok.baseclass()
    grok.name('none.jpg')
    download_name = u'none.jpg'

    def render(self):
        # A filename chooser turns a context into a filename suitable
        # for file storage.
        image = getUtility(IExtFileStore).getFileByContext(
            self.context, attr=self.download_name)
        if image is None:
            # show placeholder image
            self.response.setHeader('Content-Type', 'image/jpeg')
            return open(DEFAULT_IMAGE_PATH, 'rb').read()
        dummy,ext = os.path.splitext(image.name)
        if ext == '.jpg':
            self.response.setHeader('Content-Type', 'image/jpeg')
        elif ext == '.fpm':
            self.response.setHeader('Content-Type', 'application/binary')
        elif ext == '.png':
            self.response.setHeader('Content-Type', 'image/png')
        elif ext == '.pdf':
            self.response.setHeader('Content-Type', 'application/pdf')
        elif ext == '.tif':
            self.response.setHeader('Content-Type', 'image/tiff')
        return image

# File viewlets for customer base page

class PassportDisplay(FileDisplay):
    """Passport display viewlet.
    """
    grok.order(1)
    grok.context(ICustomer)
    grok.view(CustomerBaseDisplayFormPage)
    grok.require('waeup.viewCustomer')
    grok.template('imagedisplay')
    label = _(u'Passport Picture')
    download_name = u'passport.jpg'


class PassportManageUpload(FileUpload):
    """Passport upload viewlet for officers.
    """
    grok.order(1)
    grok.context(ICustomer)
    grok.view(CustomerBaseManageFormPage)
    grok.require('waeup.manageCustomer')
    grok.template('imageupload')
    label = _(u'Passport Picture (jpg only)')
    mus = 1024 * 50
    download_name = u'passport.jpg'
    tab_redirect = '#tab2'


class PassportEditUpload(PassportManageUpload):
    """Passport upload viewlet for customers.
    """
    grok.view(CustomerFilesUploadPage)
    grok.require('waeup.uploadCustomerFile')


class Passport(Image):
    """Renders jpeg passport picture.
    """
    grok.name('passport.jpg')
    download_name = u'passport.jpg'
    grok.context(ICustomer)
    grok.require('waeup.viewCustomer')

# File viewlets for customer documents

class SampleScanManageUpload(FileUpload):
    """Scan upload viewlet for officers.
    """
    grok.order(1)
    grok.context(CustomerSampleDocument)
    grok.view(DocumentManageFormPage)
    grok.require('waeup.manageCustomer')
    label = _(u'Sample Scan')
    title = _(u'Sample Scan')
    mus = 1024 * 50
    download_name = u'sample'
    tab_redirect = '#tab2'


class SampleScanEditUpload(SampleScanManageUpload):
    """Scan upload viewlet for customer.
    """
    grok.view(DocumentEditFormPage)
    grok.require('waeup.handleCustomer')


class SampleScanDisplay(FileDisplay):
    """Scan display viewlet.
    """
    grok.order(1)
    grok.context(CustomerSampleDocument)
    grok.require('waeup.viewCustomer')
    grok.view(DocumentDisplayFormPage)
    label = _(u'Sample Scan')
    title = _(u'Sample Scan')
    download_name = u'sample'


class SampleScanImage(Image):
    """Scan document.
    """
    grok.name('sample')
    grok.context(CustomerSampleDocument)
    grok.require('waeup.viewCustomer')
    download_name = u'sample'

class SampleScanPDFSlip(SampleScanDisplay):
    grok.view(PDFDocumentSlipPage)


class PDFScanManageUpload(FileUpload):
    """Scan upload viewlet for officers.
    """
    grok.view(DocumentManageFormPage)
    grok.context(CustomerPDFDocument)
    grok.require('waeup.manageCustomer')
    label = _(u'PDF File')
    title = _(u'PDF File')
    mus = 1024 * 200
    download_name = u'sample.pdf'
    tab_redirect = '#tab2'


class PDFScanEditUpload(PDFScanManageUpload):
    """Scan upload viewlet for customer.
    """
    grok.view(DocumentEditFormPage)
    grok.require('waeup.handleCustomer')


class PDFScanDisplay(FileDisplay):
    """Scan display viewlet.
    """
    grok.order(1)
    grok.context(CustomerPDFDocument)
    grok.require('waeup.viewCustomer')
    grok.view(DocumentDisplayFormPage)
    label = _(u'PDF Scan')
    title = _(u'PDF Scan')
    download_name = u'sample.pdf'


class PDFScanImage(Image):
    """Scan document.
    """
    grok.name('sample.pdf')
    grok.context(CustomerPDFDocument)
    grok.require('waeup.viewCustomer')
    download_name = u'sample.pdf'

class PDFScanSlip(PDFScanDisplay):
    grok.view(PDFDocumentSlipPage)
