import tempfile
import posixpath
 
from zope import event
from zExceptions import MethodNotAllowed
from ZPublisher.Iterators import IStreamIterator
from Products.CMFCore.utils import getToolByName
 
from Products.Archetypes.event import WebDAVObjectInitializedEvent
from Products.Archetypes.event import WebDAVObjectEditedEvent
from Products.Archetypes.utils import shasattr, mapply
from zope.interface import implements, Interface
 
 
class PdataStreamIterator(object):
 
    implements(IStreamIterator)
 
    def __init__(self, data, size, streamsize=1 << 16):
        # Consume the whole data into a TemporaryFile when
        # constructing, otherwise we might end up loading the whole
        # file in memory or worse, loading objects after the
        # connection is closed.
        f = tempfile.TemporaryFile(mode='w+b')
 
        while data is not None:
            f.write(data.data)
            data = data.next
 
        assert size == f.tell(), 'Informed length does not match real length'
        f.seek(0)
 
        self.file = f
        self.size = size
        self.streamsize = streamsize
 
    def __iter__(self):
        return self
 
    def next(self):
        data = self.file.read(self.streamsize)
        if not data:
            self.file.close()
            raise StopIteration
        return data
 
    def __len__(self):
        return self.size
 
_marker = []
 
 
def collection_check(self):
    if not shasattr(self, '__dav_marshall__'):
        # Backwards-compatible, if property not set ignore.
        return
    if not self.__dav_marshall__:
        # If property is set to a false value, do not allow
        # marshalling.
        raise MethodNotAllowed, 'Method not supported.'
 
 
def PUT(self, REQUEST=None, RESPONSE=None):
    """ HTTP PUT handler with marshalling support
    """
    if not REQUEST:
        REQUEST = self.REQUEST
    if not RESPONSE:
        RESPONSE = REQUEST.RESPONSE
    if not self.Schema().hasLayer('marshall'):
        RESPONSE.setStatus(501)  # Not implemented
        return RESPONSE
 
    self.dav__init(REQUEST, RESPONSE)
    collection_check(self)
 
    self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
    is_new_object = self.checkCreationFlag()
 
    file = REQUEST.get('BODYFILE', _marker)
    if file is _marker:
        data = REQUEST.get('BODY', _marker)
        if data is _marker:
            raise AttributeError, 'REQUEST neither has a BODY nor a BODYFILE'
    else:
        data = ''
        file.seek(0)
 
    mimetype = REQUEST.get_header('content-type', None)
    # mimetype, if coming from request can be like:
    # text/plain; charset='utf-8'
    #
    # XXX we should really parse the extra params and pass them on as
    # keyword arguments.
    if mimetype is not None:
        mimetype = str(mimetype).split(';')[0].strip()
 
    filename = posixpath.basename(REQUEST.get('PATH_INFO', self.getId()))
    # XXX remove after we are using global services
    # use the request to find an object in the traversal hierachy that is
    # able to acquire a mimetypes_registry instance
    # This is a hack to avoid the acquisition problem on FTP/WebDAV object
    # creation
    parents = (self,) + tuple(REQUEST.get('PARENTS', ()))
    context = None
    for parent in parents:
        if getToolByName(parent, 'mimetypes_registry', None) is not None:
            context = parent
            break
 
    # Marshall the data
    marshaller = self.Schema().getLayerImpl('marshall')
 
    args = [self, data]
    kwargs = {'file': file,
              'context': context,
              'mimetype': mimetype,
              'filename': filename,
              'REQUEST': REQUEST,
              'RESPONSE': RESPONSE}
    ddata = mapply(marshaller.demarshall, *args, **kwargs)
 
    if (shasattr(self, 'demarshall_hook') and self.demarshall_hook):
        self.demarshall_hook(ddata)
    self.manage_afterPUT(data, marshall_data=ddata, **kwargs)
    self.reindexObject()
    self.unmarkCreationFlag()
 
    if is_new_object:
        event.notify(WebDAVObjectInitializedEvent(self))
    else:
        event.notify(WebDAVObjectEditedEvent(self))
 
    RESPONSE.setStatus(204)
    return RESPONSE
 
 
def manage_FTPget(self, REQUEST=None, RESPONSE=None):
    """Get the raw content for this object (also used for the WebDAV source)
    """
 
    if REQUEST is None:
        REQUEST = self.REQUEST
 
    if RESPONSE is None:
        RESPONSE = REQUEST.RESPONSE
 
    if not self.Schema().hasLayer('marshall'):
        RESPONSE.setStatus(501)  # Not implemented
        return RESPONSE
 
    self.dav__init(REQUEST, RESPONSE)
    collection_check(self)
 
    marshaller = self.Schema().getLayerImpl('marshall')
    ddata = marshaller.marshall(self, REQUEST=REQUEST, RESPONSE=RESPONSE)
    if (shasattr(self, 'marshall_hook') and self.marshall_hook):
        ddata = self.marshall_hook(ddata)
 
    content_type, length, data = ddata
 
    RESPONSE.setHeader('Content-Type', content_type)
    # Only set Content-Length header length is not None. If we want to
    # support 'chunked' Transfer-Encoding we shouldn't set
    # this. However ExternalEditor *expects* it to be set if we return
    # a StreamIterator, so until that gets fixed we must set it.
    if length is not None:
        RESPONSE.setHeader('Content-Length', length)
 
    if type(data) is type(''):
        return data
 
    # We assume 'data' is a 'Pdata chain' as used by OFS.File and
    # return a StreamIterator.
    assert length is not None, 'Could not figure out length of Pdata chain'
    if (issubclass(IStreamIterator, Interface) and IStreamIterator.providedBy(data)
        or not issubclass(IStreamIterator, Interface) and IStreamIterator.IsImplementedBy(data)):
        return data
    return PdataStreamIterator(data, length)
 
 
def manage_afterPUT(self, data, marshall_data, file, context, mimetype,
                    filename, REQUEST, RESPONSE):
    """After webdav/ftp PUT method
    """
    pass