##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# 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.
#
##############################################################################

from pkg_resources import DistributionNotFound
from pkg_resources import get_distribution

try:
    get_distribution('five.globalrequest')
except DistributionNotFound:
    _GLOBALREQUEST_INSTALLED = False
else:
    _GLOBALREQUEST_INSTALLED = True

from .interfaces import ICatalogBrain
from Acquisition import aq_base
from Acquisition import aq_get
from Acquisition import aq_parent
from Acquisition import Implicit
from Record import Record
if _GLOBALREQUEST_INSTALLED:
    from zope.globalrequest import getRequest
from zope.interface import implements
from ZPublisher.BaseRequest import RequestContainer


class AbstractCatalogBrain(Record, Implicit):
    """Abstract base brain that handles looking up attributes as
    required, and provides just enough smarts to let us get the URL, path,
    and cataloged object without having to ask the catalog directly.
    """
    implements(ICatalogBrain)

    def has_key(self, key):
        return key in self.__record_schema__

    def __contains__(self, name):
        return name in self.__record_schema__

    def getPath(self):
        """Get the physical path for this record"""
        return aq_parent(self).getpath(self.data_record_id_)

    def getURL(self, relative=0):
        """Generate a URL for this record"""
        request = aq_get(self, 'REQUEST', None)
        if request is None and _GLOBALREQUEST_INSTALLED:
            request = getRequest()
        return request.physicalPathToURL(self.getPath(), relative)

    def _unrestrictedGetObject(self):
        """Return the object for this record

        Same as getObject, but does not do security checks.
        """
        parent = aq_parent(self)
        if (aq_get(parent, 'REQUEST', None) is None
            and _GLOBALREQUEST_INSTALLED):
            request = getRequest()
            if request is not None:
                # path should be absolute, starting at the physical root
                parent = self.getPhysicalRoot()
                request_container = RequestContainer(REQUEST=request)
                parent = aq_base(parent).__of__(request_container)
        return parent.unrestrictedTraverse(self.getPath())

    def getObject(self, REQUEST=None):
        """Return the object for this record

        Will return None if the object cannot be found via its cataloged path
        (i.e., it was deleted or moved without recataloging), or if the user is
        not authorized to access the object.

        This method mimicks a subset of what publisher's traversal does,
        so it allows access if the final object can be accessed even
        if intermediate objects cannot.
        """
        path = self.getPath().split('/')
        if not path:
            return None
        parent = aq_parent(self)
        if (aq_get(parent, 'REQUEST', None) is None
            and _GLOBALREQUEST_INSTALLED):
            request = getRequest()
            if request is not None:
                # path should be absolute, starting at the physical root
                parent = self.getPhysicalRoot()
                request_container = RequestContainer(REQUEST=request)
                parent = aq_base(parent).__of__(request_container)
        if len(path) > 1:
            parent = parent.unrestrictedTraverse(path[:-1])

        return parent.restrictedTraverse(path[-1])

    def getRID(self):
        """Return the record ID for this object."""
        return self.data_record_id_


class NoBrainer:
    """ This is an empty class to use when no brain is specified. """
    pass
