import logging
from ZODB.loglevels import TRACE

from AccessControl import ClassSecurityInfo
from AccessControl import getSecurityManager
from Acquisition import aq_parent
from Acquisition import aq_inner
from Globals import InitializeClass

from Products.CMFCore.utils import getToolByName
from Products.CMFCore.WorkflowCore import ObjectMoved
from Products.CMFCore.WorkflowCore import ObjectDeleted
from Products.CMFCore.WorkflowCore import WorkflowException

from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition
from Products.DCWorkflow.Transitions import TRIGGER_USER_ACTION

#from expression import CPSStateChangeInfo as StateChangeInfo
#from expression import createExprContext

#from zope.interface import implements
#from Products.CPSWorkflow.interfaces import ICPSWorkflowDefinition

from Products.CPSCore.ProxyBase import ProxyBase
from Products.CPSCore.EventServiceTool import getEventService

#from constants import TRANSITION_FLAGS_EXPORT

#
# Backwards compatibility with CPS3 <= 3.2.x
# Importing transition flags from here before.
#

from Products.CPSWorkflow.transitions import *
from Products.CPSWorkflow.states import *


logger = logging.getLogger('CPSWorkflow.workflow')
security = ClassSecurityInfo()

security.declarePrivate('updateStackDefinitionsRoleMappingFor')
def updateStackDefinitionsRoleMappingsFor(self, ob, **kw):
    """Update local roles for delegatees that are within workflow stacks
    """
    current_wf_var_id = kw.get('current_wf_var_id', '')

    wftool = getToolByName(self, 'portal_workflow')
    stackdefs = wftool.getStackDefinitionsFor(ob)

    changed = 0

    #
    # First save the former local roles maping They will be
    # overriden if defined on the state just after.  For the other
    # ones, we need to keep the flrm's since the stackdef might be
    # defined on another state further within the process
    # definition or called by a stack flaged transition.
    #
    tdef = kw.get('tdef')
    history = wftool.getHistoryOf(self.id, ob)
    if history is not None:
        try:
            former_status = history[-2]
        except IndexError:
            # Virgin instance ;)
            pass
        else:
            sflrm = former_status.get('sflrm', {})
            for k, v in sflrm.items():
                wftool.updateFormerLocalRoleMappingForStack(ob, self.id,
                                                            k, v)

    #
    # Let's ask the stack definition for the list of local roles
    #

    for stackdef in stackdefs.keys():

        # Update only the variable on wich the transition just applied
        if not stackdef == current_wf_var_id:
            continue

        ds = wftool.getInfoFor(ob, stackdef, self.id)
        former_mapping = wftool.getFormerLocalRoleMappingForStack(
            ob, self.id, stackdef)
        mapping = stackdefs[stackdef]._getLocalRolesMapping(ds)

        #
        # First remove associated local roles to the ones who are not
        # supposed to have any (not a single one)
        #

        for id in former_mapping.keys():
            old_local_roles = former_mapping[id]

            # Remove all previous local roles in this case
            if id not in mapping.keys():
                roles_to_remove = old_local_roles

            # Remove only local roles that are only within the
            # previous mapping
            else:
                roles_to_remove = []
                for old_role in old_local_roles:
                    if old_role not in mapping[id]:
                        roles_to_remove.append(old_role)


            # Check that no other stack distributes currently this role
            # to the current member
            roles_to_keep = ()
            for old_local_role in roles_to_remove:
                stacks = wftool.getStacks(ob)
                for stack_id, stack in stacks.items():
                    if stack_id != current_wf_var_id:
                        # The current transition is already
                        # executed on ob thus we can take the
                        # former mapping for this other given
                        if (old_local_role in
                            wftool.getFormerLocalRoleMappingForStack(
                            ob, self.id, stack_id).get(id, ())):
                            roles_to_keep += (old_local_role,)

            # Do the actual job to remove absolete roles
            for old_local_role in roles_to_remove:

                # Don't remove those already managed by another stack
                if old_local_role in roles_to_keep:
                    continue

                if not id.startswith('group:'):
                    current_roles = list(
                        ob.get_local_roles_for_userid(userid=id))
                    new_roles = [role for role in current_roles
                                 if role != old_local_role]
                    ob.manage_delLocalRoles(userids=[id])
                    if new_roles:
                        ob.manage_setLocalRoles(id, new_roles)
                else:
                    id = id[len('group:'):]
                    current_roles = list(
                        ob.get_local_roles_for_groupid(groupid=id))
                    new_roles = [role for role in current_roles
                                 if role != old_local_role]
                    ob.manage_delLocalGroupRoles(groupids=[id])
                    if new_roles:
                        ob.manage_setLocalGroupRoles(id, new_roles)

            # Update ?
            if roles_to_remove:
                changed = 1

        #
        # Add local roles to member / groups within the mapping if
        # they don't have the roles already in their merged local roles
        #

        for id in mapping.keys():
            changed = 1
            local_roles = mapping[id]
            if not id.startswith('group:'):
                current_roles = ob.get_local_roles_for_userid(userid=id)
                new_roles = list(current_roles)
                for role in local_roles:
                    if role not in new_roles:
                        new_roles.append(role)
                ob.manage_setLocalRoles(id, new_roles)
            else:
                id = id[len('group:'):]
                current_roles = ob.get_local_roles_for_groupid(groupid=id)
                new_roles = list(current_roles)
                for role in local_roles:
                    if role not in new_roles:
                        new_roles.append(role)
                ob.manage_setLocalGroupRoles(id, new_roles)

        # Update the former local role mapping
        wftool.updateFormerLocalRoleMappingForStack(ob, self.id, stackdef,
                                                    mapping)
    return changed

from Products.CPSWorkflow.workflow import WorkflowDefinition
WorkflowDefinition.updateStackDefinitionsRoleMappingsFor = updateStackDefinitionsRoleMappingsFor
