##############################################################################
#
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""$Id$
"""

# Zope External Editor Product by Casey Duncan

from string import join  # For Zope 2.3 compatibility
import types
import urllib
from Acquisition import aq_inner, aq_base, aq_parent, Implicit
try:
    from App.class_init import InitializeClass
except ImportError:
    from App.class_init import default__class_init__ as InitializeClass
from App.Common import rfc1123_date
from AccessControl.SecurityManagement import getSecurityManager
from AccessControl.SecurityInfo import ClassSecurityInfo
from OFS import Image
try:
    from webdav.Lockable import wl_isLocked
except ImportError:
    # webdav module not available
    def wl_isLocked(ob):
        return 0

from ZPublisher.Iterators import IStreamIterator
from zope.interface import implements, Interface
HAVE_Z3_IFACE = issubclass(IStreamIterator, Interface)

ExternalEditorPermission = 'Use external editor'
from collective.filepreviewbehavior.interfaces import IPreviewable

_callbacks = []

class PDataStreamIterator:
    if HAVE_Z3_IFACE:
        implements(IStreamIterator)
    else:
        __implements__ = IStreamIterator

    def __init__(self, data):
        self.data = data

    def __iter__(self):
        return self

    def next(self):
        if self.data is None:
            raise StopIteration
        data = self.data.data
        self.data = self.data.next
        return data


def registerCallback(cb):
    """Register a callback to be called by the External Editor when
    it's about to be finished with collecting metadata for the
    to-be-edited file to allow actions to be taken, like for example
    inserting more metadata headers or enabling response compression
    or anything.
    """
    _callbacks.append(cb)


def applyCallbacks(ob, metadata, request, response):
    """Apply the registered callbacks in the order they were
    registered. The callbacks are free to perform any operation,
    including appending new metadata attributes and setting response
    headers.
    """
    for cb in _callbacks:
        cb(ob, metadata, request, response)





def index_html(self, REQUEST, RESPONSE, path=None):
        """Publish the object to the external editor helper app"""

        security = getSecurityManager()
        if path is None:
            parent = self.aq_parent
            try:
                ob = parent[REQUEST['target']]  # Try getitem
            except KeyError:
                ob = getattr(parent, REQUEST['target'])  # Try getattr
            except AttributeError:
                # Handle objects that are methods in ZClasses
                ob = parent.propertysheets.methods[REQUEST['target']]
        else:
            ob = self.restrictedTraverse(path)

        r = []
        r.append('url:%s' % ob.absolute_url())
#         import pdb
#         pdb.set_trace()
        r.append('meta_type:%s' % ob.meta_type)

        title = getattr(aq_base(ob), 'title', None)
        if title is not None:
            if callable(title):
                title = title()
            if isinstance(title, types.UnicodeType):
                title = unicode.encode(title, 'utf-8')
            r.append('title:%s' % title)

#         if hasattr(aq_base(ob), 'content_type'):

        if hasattr(aq_base(ob), 'file'):            
#             if callable(ob.content_type):
            
            preadapter = IPreviewable(ob)
            ftype = preadapter.getFileType()
#             ffield = aq_base(ob).file

          
            r.append('content_type:%s' % ftype)


        if REQUEST._auth:
            if REQUEST._auth[-1] == '\n':
                auth = REQUEST._auth[:-1]
            else:
                auth = REQUEST._auth

            r.append('auth:%s' % auth)

        r.append('cookie:%s' % REQUEST.environ.get('HTTP_COOKIE', ''))

        if wl_isLocked(ob):
            # Object is locked, send down the lock token
            # owned by this user (if any)
            user_id = security.getUser().getId()
            for lock in ob.wl_lockValues():
                if not lock.isValid():
                    continue  # Skip invalid/expired locks
                creator = lock.getCreator()
                if creator and creator[1] == user_id:
                    # Found a lock for this user, so send it
                    r.append('lock-token:%s' % lock.getLockToken())
                    if REQUEST.get('borrow_lock'):
                        r.append('borrow_lock:1')
                    break

        # Apply any extra callbacks that might have been registered.
        applyCallbacks(ob, r, REQUEST, RESPONSE)

        # Finish metadata with an empty line.
        r.append('')
        metadata = join(r, '\n')
        metadata_len = len(metadata)

        # Check if we should send the file's data down the response.
        if REQUEST.get('skip_data'):
            # We've been requested to send only the metadata. The
            # client will presumably fetch the data itself.
            self._write_metadata(RESPONSE, metadata, metadata_len)
            return ''

        ob_data = getattr(aq_base(ob), 'data', None)
        if (ob_data is not None and isinstance(ob_data, Image.Pdata)):
            # We have a File instance with chunked data, lets stream it.
            #
            # Note we are setting the content-length header here. This
            # is a simplification. Read comment below.
            #
            # We assume that ob.get_size() will return the exact size
            # of the PData chain. If that assumption is broken we
            # might have problems. This is mainly an optimization. If
            # we read the whole PData chain just to compute the
            # correct size that could cause the whole file to be read
            # into memory.
            RESPONSE.setHeader('Content-Length', ob.get_size())
            # It is safe to use this PDataStreamIterator here because
            # it is consumed right below. This is only used to
            # simplify the code below so it only has to deal with
            # stream iterators or plain strings.
            body = PDataStreamIterator(ob.data)
#         elif getattr(aq_base(ob),'file',None):
#             body = aq_base(ob).file.data
        elif hasattr(ob, 'manage_FTPget'):
            # Calling manage_FTPget *might* have side-effects. For
            # example, in Archetypes it does set the 'content-type'
            # response header, which would end up overriding our own
            # content-type header because we've set it 'too
            # early'. We've moved setting the content-type header to
            # the '_write_metadata' method since, and any manipulation
            # of response headers should happen there, if possible.
            try:
                body = ob.manage_FTPget()
#                 import pdb
#                 pdb.set_trace()
            except TypeError:  # some need the R/R pair!
                body = ob.manage_FTPget(REQUEST, RESPONSE)
        elif hasattr(ob, 'EditableBody'):
            body = ob.EditableBody()
        elif hasattr(ob, 'document_src'):
            body = ob.document_src(REQUEST, RESPONSE)
        elif hasattr(ob, 'read'):
            body = ob.read()
        else:
            # can't read it!
            raise 'BadRequest', 'Object does not support external editing'

        if (HAVE_Z3_IFACE and IStreamIterator.providedBy(body) or
                (not HAVE_Z3_IFACE) and IStreamIterator.isImplementedBy(body)):
            # We need to manage our content-length because we're streaming.
            # The content-length should have been set in the response by
            # the method that returns the iterator, but we need to fix it up
            # here because we insert metadata before the body.
            clen = RESPONSE.headers.get('content-length', None)
            assert clen is not None
            self._write_metadata(RESPONSE, metadata, metadata_len + int(clen))
#             import pdb
#             pdb.set_trace()
#             i = 0
            for data in body:
#                 if i != 0:
                    RESPONSE.write(data)
#                 else:                    
#                     i= i+1
#                     continue                    
            return ''

        # If we reached this point, body *must* be a string. We *must*
        # set the headers ourselves since _write_metadata won't get
        # called.
        self._set_headers(RESPONSE)
        return join((metadata, body), '\n')
