#
#  Copyright (c) 2013 Helmut Merz helmutm@cy55.de
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

"""
Utilities for the loops.organize package.
"""

from zope import interface, component, schema
from zope.app.authentication.interfaces import IPluggableAuthentication
from zope.app.authentication.interfaces import IAuthenticatorPlugin
from zope.app.authentication.groupfolder import GroupFolder
from zope.app.security.interfaces import IAuthentication, PrincipalLookupError
from zope.app.security.settings import Allow, Deny, Unset
from zope.securitypolicy.interfaces import IPrincipalRoleManager
from zope.traversing.api import getParents
from loops.common import adapted
from loops.security.common import getCurrentPrincipal
from loops.type import getOptionsDict

defaultAuthPluginId = 'loops'


def getPrincipalFolder(context=None, authPluginId=None, ignoreErrors=False):
    pau = component.getUtility(IAuthentication, context=context)
    if not IPluggableAuthentication.providedBy(pau):
        if ignoreErrors:
            return None
        raise ValueError(u'There is no pluggable authentication '
                          'utility available.')
    if authPluginId is None and context is not None:
        person = context.getLoopsRoot().getConceptManager()['person']
        od = getOptionsDict(adapted(person).options)
        authPluginId = od.get('principalfolder', defaultAuthPluginId)
    if authPluginId is None:
        authPluginId = defaultAuthPluginId
    if authPluginId not in pau.authenticatorPlugins:
        if ignoreErrors:
            return None
        raise ValueError(u"There is no loops authenticator "
                          "plugin '%s' available." % authPluginId)
    for name, plugin in pau.getAuthenticatorPlugins():
        if name == authPluginId:
            return plugin


def getGroupsFolder(context=None, name='gloops', create=False):
    gf = getPrincipalFolder(authPluginId=name, ignoreErrors=True)
    if gf is None and create:
        pau = component.getUtility(IAuthentication, context=context)
        gf = pau[name] = GroupFolder(name + '.')
        pau.authenticatorPlugins = tuple(
                        list(pau.authenticatorPlugins) + [name])
    return gf


def getGroupId(group):
    gf = group.__parent__
    return ''.join((gf.__parent__.prefix, gf._groupid(group)))


def getInternalPrincipal(id, context=None, pau=None):
    if pau is None:
        pau = component.getUtility(IAuthentication, context=context)
    if not IPluggableAuthentication.providedBy(pau):
        raise ValueError(u'There is no pluggable authentication '
                         u'utility available.')
    if not id.startswith(pau.prefix):
        next = queryNextUtility(pau, IAuthentication)
        if next is None:
            raise PrincipalLookupError(id)
        #return next.getPrincipal(id)
        return getInternalPrincipal(id, context, pau=next)
    id = id[len(pau.prefix):]
    for name, authplugin in pau.getAuthenticatorPlugins():
        if not id.startswith(authplugin.prefix):
            continue
        principal = authplugin.get(id[len(authplugin.prefix):])
        if principal is None:
            continue
        return principal
    next = queryNextUtility(pau, IAuthentication)
    if next is not None:
        #return next.getPrincipal(pau.prefix + id)
        return getInternalPrincipal(id, context, pau=next)
    raise PrincipalLookupError(id)


def getPrincipalForUserId(id, context=None):
    auth = component.getUtility(IAuthentication, context=context)
    try:
        return auth.getPrincipal(id)
    except PrincipalLookupError:
        return None


def getRolesForPrincipal(id, context):
    prinrole = IPrincipalRoleManager(context, None)
    if prinrole is None:
        return []
    result = []
    denied = []
    for role, setting in prinrole.getRolesForPrincipal(id):
        if setting == Allow:
            result.append(role)
        elif setting == Deny:
            denied.append(role)
    for obj in getParents(context):
        prinrole = IPrincipalRoleManager(obj, None)
        if prinrole is not None:
            for role, setting in prinrole.getRolesForPrincipal(id):
                if setting == Allow and role not in denied and role not in result:
                    result.append(role)
                elif setting == Deny and role not in denied:
                    denied.append(role)
    return result


def getGroupsForPrincipal(principal=None):
    if principal is None:
        principal = getCurrentPrincipal()
    gf = getGroupsFolder()
    prefix = 'gloops.'
    return [(g.startswith(prefix) and g[len(prefix):] or g)
            for g in gf.getGroupsForPrincipal(principal.id)]


def getTrackingStorage(obj, name):
    records = obj.getLoopsRoot().getRecordManager()
    if records is not None:
        return records.get(name)
    return None

