from unittest import TestSuite, makeSuite, main
import Testing
try:
    import Zope2
except ImportError: # BBB: for Zope 2.7
    import Zope as Zope2
Zope2.startup()

from Products.CMFCore.interfaces.IOpaqueItems \
    import ICallableOpaqueItem, ICallableOpaqueItemEvents
from Products.CMFCore.PortalFolder import PortalFolder
from Products.CMFCore.tests.base.dummy \
    import DummyContent as OriginalDummyContent
from Products.CMFCore.tests.base.testcase import SecurityTest
from Products.CMFCore.TypesTool import TypesTool


# -------------------------------------------
# Helper classes and functions
# -------------------------------------------

def extra_meta_types():
    return [{'name': 'Dummy', 'action': 'manage_addFolder'}]

def addDummyContent(container, id, opaqueItem):
    container._setObject(id, DummyContent(id, opaqueItem=opaqueItem, catalog=1))
    return getattr(container, id)


class DummyContent(OriginalDummyContent):
    """ A Dummy piece of PortalContent with additional attributes
    """

    def __init__(self, id='dummy', opaqueItem=None, *args, **kw):
        OriginalDummyContent.__init__(self, id, *args, **kw)
        if opaqueItem is None:
            self.opaqueItem = 'noncallable'
            self.opaqueItemsId = 'opaqueItem'
        elif isinstance(opaqueItem, basestring):
            Hooks(self, opaqueItem)
            self.opaqueItemsId = opaqueItem
        else:
            opaqueItem(self, 'opaqueItem')
            self.opaqueItemsId = 'opaqueItem'

    # Ensure additional attributes get copied
    def _getCopy(self, container):
        obj = DummyContent(self.id, catalog=self.catalog)
        setattr(obj, self.opaqueItemsId, getattr(self, self.opaqueItemsId))
        return obj

    def isNotifiedByAfterAdd(self):
        return getattr(getattr(self, self.opaqueItemsId), 'addCount', None)

    def isNotifiedByAfterClone(self):
        return getattr(getattr(self, self.opaqueItemsId), 'cloneCount', None)

    def isNotifiedByBeforeDelete(self):
        return getattr(getattr(self, self.opaqueItemsId), 'deleteCount', None)


class OpaqueBase:
    """ Dummy opaque item without manage_after/before hookes
    """
    def __init__(self, obj, id):
        self.addCount = self.cloneCount = self.deleteCount = 0
        self.addCounter = self.cloneCounter = self.deleteCounter = 1
        self.id = id
        setattr(obj, id, self)
        
    def __call__():
        return
        
    def getId(self):
        return self.id

class Marker(OpaqueBase):
    """ Opaque item without manage_after/before hookes but marked as callable
    """
    __implements__ = (
        ICallableOpaqueItem,
    )

class Hooks(OpaqueBase):
    """ Opaque item with manage_after/before hooks but not marked as callable
    """
    __implements__ = (
        ICallableOpaqueItemEvents,
    )
    
    def manage_afterAdd(self, item, container):
        self.addCount = self.addCounter
        self.addCounter += 1

    def manage_afterClone(self, item):
        self.cloneCount = self.cloneCounter
        self.cloneCounter += 1

    def manage_beforeDelete(self, item, container):
        self.deleteCount = self.deleteCounter
        self.deleteCounter += 1


class MarkerAndHooks(Marker, Hooks):
    """ Opaque item with manage_after/before hookes and marked as callable
    """
    __implements__ = Marker.__implements__ + Hooks.__implements__


# -------------------------------------------
# Unit Tests
# -------------------------------------------

class ManageBeforeAfterTests(SecurityTest):

    def setUp(self):
        SecurityTest.setUp(self)

        root = self.root

        # setting up types tool
        root._setObject( 'portal_types', TypesTool() )

        # setup portal
        try: root._delObject('test')
        except AttributeError: pass
        root._setObject('test', PortalFolder('test', ''))
        self.test = test = self.root.test

        # setting up folders
        test._setObject( 'folder', PortalFolder( 'folder', '' ) )
        folder = self.folder = test.folder
        folder._setObject( 'sub', PortalFolder( 'sub', '' ) )
        sub = self.sub = folder.sub

        #----- hacks to allow pasting (see also test_PortalFolder)
        # WAAA! force sub to allow paste of Dummy object.
        sub.all_meta_types = extra_meta_types()

        # delete items if necessary
        try: folder._delObject('dummy')
        except AttributeError: pass
        try: sub._delObject('dummy')
        except AttributeError: pass

    def test_nonCallableItem(self):
        # no exception should be raised
        folder = self.folder
        sub = self.sub
        dummy = addDummyContent(folder, 'dummy', None)

        # WAAAA! must get _p_jar set
        old, dummy._p_jar = sub._p_jar, self.root._p_jar
        try:
            cp = folder.manage_copyObjects(ids=['dummy'])
            sub.manage_pasteObjects(cp)
        finally:
            dummy._p_jar = old

    def test_callableItemWithMarkerOnly(self):
        folder = self.folder
        sub = self.sub
        dummy = addDummyContent(folder, 'dummy', Marker)

        self.failIf(dummy.isNotifiedByAfterAdd())
        self.failIf(dummy.isNotifiedByAfterClone())
        self.failIf(dummy.isNotifiedByBeforeDelete())

        # WAAAA! must get _p_jar set
        old, dummy._p_jar = sub._p_jar, self.root._p_jar
        try:
            cp = folder.manage_copyObjects(ids=['dummy'])
            sub.manage_pasteObjects(cp)
        finally:
            dummy._p_jar = old

        self.failIf(dummy.isNotifiedByAfterAdd())
        self.failIf(dummy.isNotifiedByAfterClone())
        self.failIf(dummy.isNotifiedByBeforeDelete())

    def test_callableItemWithHooksOnly(self):
        folder = self.folder
        sub = self.sub
        dummy = addDummyContent(folder, 'dummy', Hooks)

        self.failIf(dummy.isNotifiedByAfterAdd())
        self.failIf(dummy.isNotifiedByAfterClone())
        self.failIf(dummy.isNotifiedByBeforeDelete())

        # WAAAA! must get _p_jar set
        old, dummy._p_jar = sub._p_jar, self.root._p_jar
        try:
            cp = folder.manage_copyObjects(ids=['dummy'])
            sub.manage_pasteObjects(cp)
        finally:
            dummy._p_jar = old

        self.failIf(dummy.isNotifiedByAfterAdd())
        self.failIf(dummy.isNotifiedByAfterClone())
        self.failIf(dummy.isNotifiedByBeforeDelete())

    def test_callableItemWithMarkerAndHooks(self):
        folder = self.folder
        sub = self.sub
        dummy = addDummyContent(folder, 'dummy', MarkerAndHooks)

        self.assertEqual(dummy.isNotifiedByAfterAdd(), 1)
        self.failIf(dummy.isNotifiedByAfterClone())
        self.failIf(dummy.isNotifiedByBeforeDelete())

        # WAAAA! must get _p_jar set
        old, dummy._p_jar = sub._p_jar, self.root._p_jar
        try:
            cp = folder.manage_copyObjects(ids=['dummy'])
            sub.manage_pasteObjects(cp)
        finally:
            dummy._p_jar = old

        self.assertEqual(dummy.isNotifiedByAfterAdd(), 2)
        self.assertEqual(dummy.isNotifiedByAfterClone(), 1)
        self.failIf(dummy.isNotifiedByBeforeDelete())

    def test_talkbackItem(self):
        folder = self.folder
        sub = self.sub

        dummy = addDummyContent(folder, 'dummy', 'talkback')

        self.assertEqual(dummy.isNotifiedByAfterAdd(), 1)
        self.failIf(dummy.isNotifiedByAfterClone())
        self.failIf(dummy.isNotifiedByBeforeDelete())

        # WAAAA! must get _p_jar set
        old, dummy._p_jar = sub._p_jar, self.root._p_jar
        try:
            cp = folder.manage_copyObjects(ids=['dummy'])
            sub.manage_pasteObjects(cp)
        finally:
            dummy._p_jar = old

        self.assertEqual(dummy.isNotifiedByAfterAdd(), 2)
        self.assertEqual(dummy.isNotifiedByAfterClone(), 1)
        self.failIf(dummy.isNotifiedByBeforeDelete())


def test_suite():
    return TestSuite((
        makeSuite(ManageBeforeAfterTests),
        ))

if __name__ == '__main__':
    main(defaultTest='test_suite')
