# -*- coding: UTF-8 -*-


# Expression
# By: Frederic Peters <fpeters@entrouvert.com>
#     Emmanuel Raviart <eraviart@entrouvert.com>
#
# Copyright (C) 2004 Entr'ouvert, Frederic Peters & Emmanuel Raviart
#
# 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.


"""Groups Module"""


import expression.core.elements as elements
import expression.core.locations as locations
import expression.core.namespaces as namespaces
import expression.core.stations as stations
import expression.core.things as things


class DataHolderPointer(elements.Element):
    def getAbsoluteUri(self):
        nodes = self.evaluateXpath("@src")
        if not nodes:
            return None
        return self.constructAbsoluteUri(nodes[0].content)
    

class Group(things.Thing):
    def contains(self, identityAbsoluteUri, authenticationMethod, indirect = True,
                 resultWhenUnknown = False):
        """Indicates whether the group contains an identity."""

        result = self.contains1(identityAbsoluteUri, authenticationMethod, indirect, {})
        if result is None:
            result = resultWhenUnknown
        return result

    def contains1(self, identityAbsoluteUri, authenticationMethod, indirect, results):
        """The method contains1 can return None, False or True."""

        absoluteUri = self.getDataHolder().getAbsoluteUri()
        if results.has_key(absoluteUri):
            return results[absoluteUri]
        results[absoluteUri] = None
        result = self.contains2(identityAbsoluteUri, authenticationMethod, indirect, results)
        results[absoluteUri] = result
        return result

    def contains2(self, identityAbsoluteUri, authenticationMethod, indirect, results):
        """The method contains2 can return None, False or True."""

        authenticationMethods = self.getAuthenticationMethods()
        if authenticationMethods is not None and authenticationMethod not in authenticationMethods:
            return False
        memberNodes = self.evaluateXpath("yep:members/*")
        if not memberNodes:
            return False
        result = False
        groupAbsoluteUris = []
        for memberNode in memberNodes:
            if memberNode.name == "authenticatedUsers":
                if identityAbsoluteUri is None:
                    continue
                authenticationMethods = memberNode.prop("authenticationMethods")
                if authenticationMethods is None or authenticationMethod in authenticationMethods:
                    return True
                continue
            if memberNode.name == "everybody":
                return True
            memberSourceNodes = self.evaluateXpath("@src", contextNode = memberNode)
            if not memberSourceNodes:
                continue
            memberAbsoluteUri = self.constructAbsoluteUri(memberSourceNodes[0].content)
            if memberAbsoluteUri == identityAbsoluteUri:
                return True
            elif memberNode.name == "group":
                groupAbsoluteUris.append(memberAbsoluteUri)
        if not indirect:
            return False
        if groupAbsoluteUris:
            groupsHolder = self.walkToLocation("/groups")
            for groupAbsoluteUri in groupAbsoluteUris:
                groupHolder = groupsHolder.getItem(locations.extractUriLocalId(groupAbsoluteUri))
                if groupHolder is None:
                    groupResult = None
                else:
                    group = groupHolder.getRootElement()
                    groupResult = group.contains1(
                        identityAbsoluteUri, authenticationMethod, indirect, results)
                if groupResult is None:
                    result = None
                elif groupResult:
                    return True
        return result

    def getAuthenticationMethods(self):
        nodes = self.evaluateXpath("yep:authenticationMethods")
        if not nodes:
            return None
        return nodes[0].content.split(" ")

    def getMembers(self):
        nodes = self.evaluateXpath("yep:members")
        if not nodes:
            return None
        return Members(nodes[0], previous = self, owner = self)

    def getName(self):
        nodes = self.evaluateXpath("yep:name")
        if nodes:
            return nodes[0].content
        else:
            return None

    def setAuthenticationMethods(self, authenticationMethods):
        nodes = self.evaluateXpath("yep:authenticationMethods")
        if authenticationMethods is None:
            for node in nodes:
                node.unlinkNode()
                node.freeNode()
        else:
            if nodes:
                node = nodes[0]
            else:
                node = self.node.newTextChild(None, "authenticationMethods", None)
            # Note: Don't use setContent, because for some nodes it tries to
            # convert entities references.
            # node.setContent(" ".join(authenticationMethods))
            if node.children is not None:
                childNode = node.children
                childNode.unlinkNode()
                childNode.freeNodeList()
            node.addChild(node.doc.newDocText(" ".join(authenticationMethods)))

    def setMembers(self, members):
        nodes = self.evaluateXpath("yep:members")
        if nodes:
            node = nodes[0]
            node.replaceNode(self.absorbNode(members))
            node.freeNode()
        else:
            self.node.addChild(self.absorbNode(members))
        if self.node.doc is not None:
            self.node.reconciliateNs(self.node.doc)

    members = property(getMembers, setMembers)
    name = property(getName)


class GroupPointer(DataHolderPointer):
    pass


class IdentityPointer(DataHolderPointer):
    pass


class Members(elements.List):
    def __iter__(self):
        nodes = self.evaluateXpath(self.itemsXpath)
        for node in nodes:
            yield self.guessItemClass(node)(node, parent = self)

    def guessItemClass(self, node):
        itemClasses = {
            "authenticatedUsers": elements.AuthenticatedUsers,
            "everybody": elements.Everybody,
            "group": GroupPointer,
            "identity": IdentityPointer,
            }
        return itemClasses[node.name]


elements.registerElement(
    namespaces.yep.uri, "group", Group, "http://www.entrouvert.org/expression/schemas/Group.xsd",
    "http://www.entrouvert.org/expression/descriptions/Group.xml")
