##############################################################################
#
# Copyright (c) 2001-2007 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.
#
##############################################################################
"""Base Mix-in class for Placeful Setups

Also contains common test related classes/functions/objects.
"""

import os
import zope.interface
import zope.site.folder
from zope.component.interfaces import IComponentLookup
from zope.app.component.interfaces import ILocalSiteManager
from zope.app.testing import setup
from zope.app.testing.placelesssetup import PlacelessSetup

from zope.app.testing.functional import ZCMLLayer
from zope.traversing.api import traverse

AppComponentLayer = ZCMLLayer(
    os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'),
    __name__, 'AppComponentLayer', allow_teardown=True)

class Place(object):

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

    def __get__(self, inst, cls=None):
        if inst is None:
            return self

        try:
            # Use __dict__ directly to avoid infinite recursion
            root = inst.__dict__['rootFolder']
        except KeyError:
            root = inst.rootFolder = setup.buildSampleFolderTree()

        return traverse(root, self.path)


class PlacefulSetup(PlacelessSetup):

    # Places :)
    rootFolder  = Place(u'')

    folder1     = Place(u'folder1')
    folder1_1   = Place(u'folder1/folder1_1')
    folder1_1_1 = Place(u'folder1/folder1_1/folder1_1_1')
    folder1_1_2 = Place(u'folder1/folder1_2/folder1_1_2')
    folder1_2   = Place(u'folder1/folder1_2')
    folder1_2_1 = Place(u'folder1/folder1_2/folder1_2_1')

    folder2     = Place(u'folder2')
    folder2_1   = Place(u'folder2/folder2_1')
    folder2_1_1 = Place(u'folder2/folder2_1/folder2_1_1')

    folder3     = Place(u"\N{CYRILLIC SMALL LETTER PE}"
                        u"\N{CYRILLIC SMALL LETTER A}"
                        u"\N{CYRILLIC SMALL LETTER PE}"
                        u"\N{CYRILLIC SMALL LETTER KA}"
                        u"\N{CYRILLIC SMALL LETTER A}3")
    folder3_1   = Place(u"\N{CYRILLIC SMALL LETTER PE}"
                        u"\N{CYRILLIC SMALL LETTER A}"
                        u"\N{CYRILLIC SMALL LETTER PE}"
                        u"\N{CYRILLIC SMALL LETTER KA}"
                        u"\N{CYRILLIC SMALL LETTER A}3/"
                        u"\N{CYRILLIC SMALL LETTER PE}"
                        u"\N{CYRILLIC SMALL LETTER A}"
                        u"\N{CYRILLIC SMALL LETTER PE}"
                        u"\N{CYRILLIC SMALL LETTER KA}"
                        u"\N{CYRILLIC SMALL LETTER A}3_1")

    def setUp(self, folders=False, site=False):
        setup.placefulSetUp()
        if folders or site:
            return self.buildFolders(site)

    def tearDown(self):
        setup.placefulTearDown()
        # clean up folders and placeful site managers and services too?

    def buildFolders(self, site=False):
        self.rootFolder = setup.buildSampleFolderTree()
        if site:
            return self.makeSite()

    def makeSite(self, path='/'):
        folder = traverse(self.rootFolder, path)
        return setup.createSiteManager(folder, True)

    def createRootFolder(self):
        self.rootFolder = zope.site.folder.rootFolder()


class SiteManagerStub(object):
    zope.interface.implements(ILocalSiteManager)

    __bases__ = ()

    def __init__(self):
        self._utils = {}

    def setNext(self, next):
        self.__bases__ = (next, )

    def provideUtility(self, iface, util, name=''):
        self._utils[(iface, name)] = util

    def queryUtility(self, iface, name='', default=None):
        return self._utils.get((iface, name), default)
    

def testingNextUtility(utility, nextutility, interface, name='',
                       sitemanager=None, nextsitemanager=None):
    """Provide a next utility for testing.

    Since utilities must be registered in sites, we really provide a next
    site manager in which we place the next utility. If you do not pass in
    any site managers, they will be created for you.

    For a simple usage of this function, see the doc test of
    `queryNextUtility()`. Here is a demonstration that passes in the services
    directly and ensures that the `__parent__` attributes are set correctly.

    First, we need to create a utility interface and implementation:

      >>> from zope.interface import Interface, implements
      >>> class IAnyUtility(Interface):
      ...     pass
      
      >>> class AnyUtility(object):
      ...     implements(IAnyUtility)
      ...     def __init__(self, id):
      ...         self.id = id
      
      >>> any1 = AnyUtility(1)
      >>> any1next = AnyUtility(2)

    Now we create a special site manager that can have a location:

      >>> SiteManager = type('SiteManager', (GlobalSiteManager,),
      ...                       {'__parent__': None})

    Let's now create one site manager

      >>> sm = SiteManager()

    and pass it in as the original site manager to the function:

      >>> testingNextUtility(any1, any1next, IAnyUtility, sitemanager=sm)
      >>> any1.__parent__ is utils
      True
      >>> smnext = any1next.__parent__
      >>> sm.__parent__.next.data['Utilities'] is smnext
      True

    or if we pass the current and the next site manager:

      >>> sm = SiteManager()
      >>> smnext = SiteManager()
      >>> testingNextUtility(any1, any1next, IAnyUtility,
      ...                    sitemanager=sm, nextsitemanager=smnext)
      >>> any1.__parent__ is sm
      True
      >>> any1next.__parent__ is smnext
      True
    
    """
    if sitemanager is None:
        sitemanager = SiteManagerStub()
    if nextsitemanager is None:
        nextsitemanager = SiteManagerStub()
    sitemanager.setNext(nextsitemanager)

    sitemanager.provideUtility(interface, utility, name)
    utility.__conform__ = (
        lambda iface:
        iface.isOrExtends(IComponentLookup) and sitemanager or None
        )
    nextsitemanager.provideUtility(interface, nextutility, name)
    nextutility.__conform__ = (
        lambda iface:
        iface.isOrExtends(IComponentLookup) and nextsitemanager or None
        )
