import logging
from Products.CMFPlone.utils import safe_unicode
from Products.Five.browser import BrowserView
from plone import api
from plone.rfc822.interfaces import IPrimaryFieldInfo
from ploneintranet import api as pi_api
from ploneintranet.attachments.attachments import IAttachmentStorage
from zExceptions import NotFound
from zope.publisher.interfaces import IPublishTraverse
from zope.interface import implementer
from plone.app.blob.download import handleRequestRange
from collective.documentviewer.settings import Settings
from webdav.common import rfc1123_date
from plone.app.blob.iterators import BlobStreamIterator
from AccessControl import getSecurityManager
from Products.CMFCore import permissions
from AccessControl import Unauthorized
from plone.app.blob.utils import openBlob
from plone.protect.auto import safeWrite
from zope.annotation.interfaces import IAnnotations

import os
log = logging.getLogger(__name__)


@implementer(IPublishTraverse)
class Attachments(BrowserView):
    """ Attachments
    """
    attachment_id = None
    preview_type = None
    page = None

    def publishTraverse(self, request, name):
        # @@attachments/{attachment_id}[/{preview_type}]
        self.attachment_id = name

        stack = request['TraversalRequestNameStack']
        if stack:
            self.preview_type = stack.pop()
        if stack:
            try:
                self.page = int(stack.pop())
            except ValueError:
                self.page = 1
        request['TraversalRequestNameStack'] = []
        return self

    def _prepare_imagedata(self, attachment, imgdata):
        r = self.request.RESPONSE
        r.setHeader('content-type', 'image/jpeg')
        r.setHeader(
            'content-disposition', 'inline; '
            'filename="{0}_preview.jpg"'.format(
                safe_unicode(self.attachment_id).encode('utf8')))
        if isinstance(imgdata, basestring):
            length = len(imgdata)
            r.setHeader('content-length', length)
            return imgdata
        else:
            length = imgdata.get_size(attachment)
            r.setHeader('content-length', length)
            blob = imgdata.get(attachment, raw=True)
            charset = 'utf-8'
            return blob.index_html(
                REQUEST=self.request, RESPONSE=r,
                charset=charset
            )

    def _prepare_pdfdata(self, pdfdata):
        r = self.request.RESPONSE
        r.setHeader('content-type', 'application/pdf')
        r.setHeader(
            'content-disposition',
            'attachment; filename="%s"' % '.'.join(
                (self.context.getId(), u'pdf')).encode('utf8'))
        if isinstance(pdfdata, basestring):
            length = len(pdfdata)
            r.setHeader('content-length', length)
            return pdfdata
        else:
            length = pdfdata.get_size(self.context)
            r.setHeader('content-length', length)
            blob = pdfdata.get(self.context, raw=True)
            charset = 'utf-8'
            return blob.index_html(
                REQUEST=self.request, RESPONSE=r,
                charset=charset
            )

    def _render_nopreview(self, attachment):
        primary_field = IPrimaryFieldInfo(attachment).value
        mimetype = primary_field.contentType
        data = primary_field.data
        self.request.response.setHeader(
            'content-type', mimetype)
        self.request.response.setHeader(
            'content-disposition', 'inline; '
            'filename="{0}"'.format(
                safe_unicode(self.attachment_id).encode('utf8')))
        return data

    def __call__(self):
        # requires View on self.context
        attachments = IAttachmentStorage(self.context)
        # separate rendering out for subclass use
        return self.render_attachments(attachments)

    def render_attachment_preview(self, attachment):
        sm = getSecurityManager()
        if not sm.checkPermission(permissions.View, self.context):
            raise Unauthorized

        r = self.request.response

        # avoid long dreaded CSRF error
        annotations = IAnnotations(attachment)
        if not annotations.get('collective.documentviewer', None):
            safeWrite(attachment)
        settings = Settings(attachment)  # possibly creates annotation

        if self.preview_type not in ('large', 'normal', 'small'):
            self.preview_type = 'small'
        if self.page is None:
            self.page = 1
        filepath = u'%s/dump_%s.%s' % (self.preview_type,
                                       self.page,
                                       settings.pdf_image_format)
        try:
            blob = settings.blob_files[filepath]
        except TypeError:
            # 'NoneType' object has no attribute '__getitem__'
            # happens e.g. when missing preview for stream attachment
            return

        blobfi = openBlob(blob)
        length = os.fstat(blobfi.fileno()).st_size
        blobfi.close()
        ext = os.path.splitext(os.path.normcase(filepath))[1][1:]
        if ext == 'txt':
            ct = 'text/plain'
        else:
            ct = 'image/%s' % ext

        r.setHeader('Content-Type', ct)
        r.setHeader('Last-Modified',
                    rfc1123_date(self.context._p_mtime))
        r.setHeader('Accept-Ranges', 'bytes')
        r.setHeader("Content-Length", length)
        request_range = handleRequestRange(self.context,
                                           length,
                                           self.request,
                                           self.request.response)
        return BlobStreamIterator(blob, **request_range)

    def render_attachments(self, attachments):
        """replaces ploneintranet/docconv/client/view.py helpers"""
        attachment = attachments[self.attachment_id]
        if not self.preview_type or self.preview_type in (
                'pdf-not-available', 'request-pdf'):
            return self._render_nopreview(attachment)

        # old way of doing things
        # upload stage
        if self.preview_type == 'pdf':
            if pi_api.previews.has_pdf():
                pdfdata = pi_api.previews.get_pdf()
                return self._prepare_pdfdata(pdfdata)
        # normal view stage
        elif self.preview_type == '@@images':
            images = api.content.get_view(
                'images',
                attachment.aq_base,
                self.request,
            )
            return self._prepare_imagedata(
                attachment,
                str(images.scale(scale='preview').data.data)
            )
        else:
            return self.render_attachment_preview(attachment)

        raise NotFound


@implementer(IPublishTraverse)
class StatusAttachments(Attachments):
    """ Attachments on a statusupdate
    """

    status_id = None

    def publishTraverse(self, request, name):
        # @@status-attachment/{status_id}/{attachment_id}[/{preview_type}]
        self.status_id = int(name)

        stack = request['TraversalRequestNameStack']
        self.attachment_id = stack.pop()
        if stack:
            self.preview_type = stack.pop()

        request['TraversalRequestNameStack'] = []
        return self

    def __call__(self):
        container = pi_api.microblog.get_microblog()
        # requires ViewStatusUpdate on the statusupdate returned
        statusupdate = container.get(self.status_id)
        attachments = IAttachmentStorage(statusupdate)
        return self.render_attachments(attachments)
