# Copyright (c) 2005 Infrae. All rights reserved.
# See also LICENSE.txt
# Zope 3
from persistent import Persistent
from zope.interface import implements
from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary

from zope.security.management import getInteraction

from zope.app import zapi
from zope.app.exception.interfaces import UserError
from zope.app.intid.interfaces import IIntIds
from zope.app.container.interfaces import INameChooser
from zope.app.container.contained import Contained
from zope.app.container.contained import NameChooser as NameChooser_
from zope.app.container.ordered import OrderedContainer
from zope.app.component.hooks import getSite
from zope.app.securitypolicy.interfaces import IPrincipalRoleManager

from documentlibrary.core.documentfolder import DocumentFolder
from documentlibrary.core import interfaces

prefix = 'documentlibrary'

class CategoryTree(OrderedContainer):
    
    implements(interfaces.ICategoryTree)

    def getOwnedCategories(self):
        result = []
        for value in self.values():
            _getOwnedCategoriesHelper(value, getInteraction(), result)
        return result

    def getOwnedCategoryIds(self):
        categories = self.getOwnedCategories()
        util = zapi.getUtility(IIntIds)
        return [util.getId(category) for category in categories]
    
def _getOwnedCategoriesHelper(object, interaction, result):
    if not interfaces.ICategory.providedBy(object):
        return
    if interaction.checkPermission(
        'documentlibrary.ApproveDocument', object):
        result.append(object)
    for value in object.values():
        _getOwnedCategoriesHelper(value, interaction, result)
    
class Category(OrderedContainer):

    implements(interfaces.ICategoryContained)
    
    def __init__(self, name, retention_period, 
                 librarians, submitters):
        super(Category, self).__init__()
        self.name = name
        self.retention_period = retention_period
        self.librarians = librarians
        self.submitters = submitters

    def getOwningLibrarians(self):
        """Get all librarians that own this category, so
        this category and above.
        """
        # remove security proxy as we want information from categories
        # we may not have the ViewCategory permission on
        from zope.security.proxy import removeSecurityProxy
        self = removeSecurityProxy(self)
        
        manager = IPrincipalRoleManager(self)
        librarians = []
        l = len(prefix)
        for principal_id, setting in manager.getPrincipalsForRole(
            'documentlibrary.CategoryOwner'):
            librarians.append(principal_id[l:])
        parent = self.__parent__
        if not interfaces.ICategory.providedBy(parent):
            return librarians
        # XXX use sets to avoid duplicates?
        return parent.getOwningLibrarians() + librarians

    def getNearestLibrarian(self):
        # remove security proxy as we want information from categories
        # we may not have the ViewCategory permission on
        from zope.security.proxy import removeSecurityProxy
        self = removeSecurityProxy(self)

        manager = IPrincipalRoleManager(self)
        librarians = []
        l = len(prefix)
        # get first librarian we can find
        for principal_id, setting in manager.getPrincipalsForRole(
            'documentlibrary.CategoryOwner'):
            return principal_id[l:]
        parent = self.__parent__
        if not interfaces.ICategory.providedBy(parent):
            return None
        return parent.getNearestLibrarian()

    def isEmpty(self):
        if len(self['documents']):
            return False
        for category in self.values():
            if not interfaces.ICategory.providedBy(category):
                continue
            if not category.isEmpty():
                return False
        return True
    
def categoryAddedHandler(event):
    object = event.object
    if interfaces.ICategory.providedBy(object):
        object['documents'] = DocumentFolder()
        
class CategoriesUtility(Persistent, Contained):
    
    implements(interfaces.IHierarchicalTermsUtility)
   
    def getTop(self):
        return getSite()['categories']

class NameChooser(NameChooser_):
    
    implements(INameChooser)

    def _name(self, object):
        return object.name.lower().replace(' ', '_')
    
    def checkName(self, name, object):
        # transform name first
        name = self._name(object)
        # then check whether name already exists (and other things)
        super(NameChooser, self).checkName(name, object)
        
    def chooseName(self, name, object):
        """Choose a unique valid name for the object

        The given name and object may be taken into account when
        choosing the name.
        """
        name = self._name(object)
        return super(NameChooser, self).chooseName(name, object)


class CategoryRoleManager(object):
    implements(interfaces.ICategoryRoleManager)
    
    def __init__(self, context):
        self.context = context
        
    def removeRoles(self):
        manager = IPrincipalRoleManager(self.context)
        for principal_id, setting in manager.getPrincipalsForRole(
            'documentlibrary.CategoryOwner'):
            manager.unsetRoleForPrincipal(
                'documentlibrary.CategoryOwner',
                principal_id)
        for principal_id, setting in manager.getPrincipalsForRole(
            'documentlibrary.CategoryContributor'):
            manager.unsetRoleForPrincipal(
                'documentlibrary.CategoryContributor',
                principal_id)

    def addRoles(self, librarians, submitters):
        manager = IPrincipalRoleManager(self.context)
        for principal_id in librarians:
            principal_id = prefix + principal_id
            manager.assignRoleToPrincipal(
                'documentlibrary.CategoryOwner',
                principal_id)
        for principal_id in submitters:
            principal_id = prefix + principal_id
            manager.assignRoleToPrincipal(
                'documentlibrary.CategoryContributor',
                principal_id)

    def setRoles(self, librarians, submitters):
        self.removeRoles()
        self.addRoles(librarians, submitters)

def restrictedVocabulary(context):
    util = zapi.getUtility(
        interfaces.IHierarchicalTermsUtility, name='categories')
    terms = _getTerms(
        util.getTop(), [], 
        check_permission='documentlibrary.ContributeToCategory')
    return SimpleVocabulary(terms)

def vocabulary(context):
    util = zapi.getUtility(
        interfaces.IHierarchicalTermsUtility, name='categories')
    terms = _getTerms(util.getTop(), [])
    return SimpleVocabulary(terms)
            
def _getTerms(context, terms, sequence=(), check_permission=None):
    if check_permission is not None:
        interaction = getInteraction()
    intid = zapi.getUtility(IIntIds, context=context)
    categories = [
        c for c in context.values() if interfaces.ICategory.providedBy(c)]
    for i in range(len(categories)):
        category = categories[i]
        if check_permission is None or interaction.checkPermission(
                check_permission, category):
            id = intid.getId(category)
            term = SimpleTerm(id, str(id), category.name)
            term.sequence = sequence + (i,) # XXX hack for widgets
            terms.append(term)
        # recursive for sub-items
        _getTerms(category, terms, sequence + (i,), check_permission)
    return terms
