from Products.Archetypes.BaseObject import BaseObject
from Products.Archetypes.Referenceable import Referenceable
from Products.Archetypes.ExtensibleMetadata import ExtensibleMetadata
from Products.Archetypes.interfaces.base import IBaseContent
from Products.Archetypes.interfaces.referenceable import IReferenceable
from Products.Archetypes.interfaces.metadata import IExtensibleMetadata
from Products.Archetypes.CatalogMultiplex import CatalogMultiplex
from Products.Archetypes.utils import shasattr, mapply

from Acquisition import aq_base
from Acquisition import aq_get
from AccessControl import ClassSecurityInfo
from Globals import InitializeClass
from OFS.History import Historical
from Products.CMFCore.utils import getToolByName
from Products.CMFCore import CMFCorePermissions
from Products.CMFCore.PortalContent import PortalContent
from OFS.PropertyManager import PropertyManager
from ZODB.POSException import ConflictError

import urllib

_marker = []

class BaseContentMixin(CatalogMultiplex,
                    BaseObject,
                    PortalContent,
                    Historical):
    """A not-so-basic CMF Content implementation that doesn't
    include Dublin Core Metadata"""

    __implements__ = IBaseContent, IReferenceable, PortalContent.__implements__

    isPrincipiaFolderish=0
    manage_options = PortalContent.manage_options + Historical.manage_options

    security = ClassSecurityInfo()

    security.declarePrivate('manage_afterAdd')
    def manage_afterAdd(self, item, container):
        BaseObject.manage_afterAdd(self, item, container)
        CatalogMultiplex.manage_afterAdd(self, item, container)

    security.declarePrivate('manage_afterClone')
    def manage_afterClone(self, item):
        BaseObject.manage_afterClone(self, item)
        CatalogMultiplex.manage_afterClone(self, item)

    security.declarePrivate('manage_beforeDelete')
    def manage_beforeDelete(self, item, container):
        BaseObject.manage_beforeDelete(self, item, container)
        CatalogMultiplex.manage_beforeDelete(self, item, container)

        #and reset the rename flag (set in Referenceable._notifyCopyOfCopyTo)
        self._v_cp_refs = None

    def _notifyOfCopyTo(self, container, op=0):
        """OFS.CopySupport notify
        """
        BaseObject._notifyOfCopyTo(self, container, op=op)
        PortalContent._notifyOfCopyTo(self, container, op=op)

    security.declareProtected(CMFCorePermissions.ModifyPortalContent, 'PUT')
    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)
        self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)

        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)

        try:
            filename = REQUEST._steps[-2] #XXX fixme, use a real name
        except ConflictError:
            raise
        except:
            filename = (getattr(file, 'filename', None) or
                        getattr(file, 'name', None) or
                        self.getId())
        filename = urllib.unquote_plus(filename)

        # 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()
        RESPONSE.setStatus(204)
        return RESPONSE

    security.declareProtected(CMFCorePermissions.View, 'manage_FTPget')
    def manage_FTPget(self, REQUEST=None, RESPONSE=None):
        "Get the raw content for this object (also used for the WebDAV SRC)"

        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

        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)
        RESPONSE.setHeader('Content-Length', length)

        if type(data) is type(''): return data

        while data is not None:
            RESPONSE.write(data.data)
            data=data.next
        
    security.declarePrivate('manage_afterPUT')    
    def manage_afterPUT(self, data, marshall_data, file, context, mimetype,
                        filename, REQUEST, RESPONSE):
        """After webdav/ftp PUT method
        """
        pass

InitializeClass(BaseContentMixin)

class BaseContent(BaseContentMixin,
                  ExtensibleMetadata,
                  PropertyManager):
    """A not-so-basic CMF Content implementation with Dublin Core
    Metadata included"""

    __implements__ = BaseContentMixin.__implements__, IExtensibleMetadata

    schema = BaseContentMixin.schema + ExtensibleMetadata.schema

    manage_options = BaseContentMixin.manage_options + \
        PropertyManager.manage_options

    def __init__(self, oid, **kwargs):
        BaseContentMixin.__init__(self, oid, **kwargs)
        ExtensibleMetadata.__init__(self)

InitializeClass(BaseContent)


BaseSchema = BaseContent.schema

__all__ = ('BaseContent', 'BaseContentMixin', 'BaseSchema', )
