########################################################################
#
# File Name:            Predicate.py
#
# Documentation:        http://docs.4suite.org/4Rdf/Inference/Predicate.py.html
#

"""

WWW: http://4suite.org/4RDF         e-mail: support@4suite.org

Copyright (c) 1999 Fourthought Inc, USA.   All Rights Reserved.
See  http://4suite.org/COPYRIGHT  for license and copyright information
"""

from Ft.Lib import Set
import Query, Common

class AndMixin:
    _dump_op = 'and'
    def primeContext(self,context):
        #For an and we let the query do most of the work so we don't touch the query
        return context.skolemVariables

    def mergeSkolemVariables(self,context,state,resultInfo):
        #An intersection
        for name in resultInfo.keys():
            #This one was modified by the search
            #The query did the interseciton for us
            context.skolemVariables[name] = resultInfo[name]

        

class OrMixin:
    _dump_op = 'or'
    def primeContext(self,context):
        #We need to store a copy of the current skolem variables
        #Then do a complete query (var = None)
        #By setting the var to None
        rt = context.skolemVariables
        context.skolemVariables = {}
        return rt

    def mergeSkolemVariables(self,context,state,resultInfos):
        #An or is the union of the two sets

        #To reset the skolems, do a union of the value in the state and the result info
        #Reset the vars
        context.skolemVariables = state.copy()
        for name in resultInfos.keys():
            #Perform the union
            if state.has_key(name) and state[name] is not None:
                context.skolemVariables[name] = Set.Union(state[name],resultInfos[name])
            else:
                context.skolemVariables[name] = resultInfos[name]

        
class NotMixin:
    _dump_op = 'not'
    def primeContext(self,context):
        #For a not we let the query do most of the work so we don't touch the query
        #It will return the intersection results
        #Then we return the difference of the original state and the results
        return context.skolemVariables.copy()

    def mergeSkolemVariables(self,context,state,resultInfos):
        #A Not, take the difference of the original state and the results
        #Reset the vars
        context.skolemVariables = state.copy()
        for name in resultInfos.keys():
            #Perform the union
            if state.has_key(name) and state[name] is not None:
                context.skolemVariables[name] = Set.Not(state[name],resultInfos[name])
            else:
                context.skolemVariables[name] = resultInfos[name]


class Predicate:
    def __init__(self, id_, arguments):
        self.id = id_
        self.arguments = arguments
        return

    def _4rdf_dump(self,indent=0):
        iStr = '\t'*indent
        op = self._dump_op
        rt = iStr + '<ril:%s id="%s">\n' % (op,self.id)
        for arg in self.arguments:
            rt = rt + arg._4rdf_dump(indent+1)
        rt = rt + iStr + "</ril:%s>\n" % op
        return rt
        

class SinglePredicate(Predicate,Query.SingleQuery):
    '''
    Stored in RDF Model as triple.  For instance
    hungry(X), if the hungry predicate is in the http://spam.com namespace
    and the UUID of the X resource is 132ad72a-f1e0-4482-82e3-e7d790d4f56
    is mapped to
    (http://spam.com#hungry, urn:uuid:132ad72a-f1e0-4482-82e3-e7d790d4f56, 1)
    '''
    def __init__(self,id_,arguments):
        Predicate.__init__(self,id_,arguments)
        Query.SingleQuery.__init__(self,id_,arguments)

    def execute(self, infEng, context):

        state = self.primeContext(context)
        queryResults = Query.SingleQuery.execute(self,infEng,context)

        arg = self.arguments[0]
        args = arg.execute(infEng,context)
        newValues = map(lambda x:x[0],queryResults)
        if arg.type == Common.ArgumentTypes.SKOLEM_VARIABLE:
            self.mergeSkolemVariables(context,state,{arg.name:newValues})

            

class DualPredicate(Predicate, Query.DualQuery):
    '''
    Stored in RDF Model as triple.  For instance
    ate(X, Y), if the ate predicate is in the http://spam.com namespace
    and the UUID of the X resource is 132ad72a-f1e0-4482-82e3-e7d790d4f56
    and the UUID of the Y resource is 23bd4d35-afa1-4be1-8d80-e34bbed9db7d
    is mapped to
    (urn:uuid:132ad72a-f1e0-4482-82e3-e7d790d4f56, http://spam.com#ate, 23bd4d35-afa1-4be1-8d80-e34bbed9db7d)
    '''
    def __init__(self,id_,arguments):
        Predicate.__init__(self,id_,arguments)
        Query.DualQuery.__init__(self,id_,arguments)

    def execute(self, infEng, context):

        state = self.primeContext(context)
        queryResults = Query.DualQuery.query(self,infEng,context)
        arg = self.arguments[0]
        args = arg.execute(infEng,context)
        arg1 = self.arguments[1]
        args1 = arg1.execute(infEng,context)
        newValues = map(lambda x:x[0],queryResults)
        newValues1 = map(lambda x:x[2],queryResults)
        resInfo = {}
        if arg.type == Common.ArgumentTypes.SKOLEM_VARIABLE:
            #update the skolem varaible
            resInfo[arg.name] = newValues
        if arg1.type == Common.ArgumentTypes.SKOLEM_VARIABLE:
            #update the skolem varaible
            resInfo[arg1.name] = newValues1

        self.mergeSkolemVariables(context,state,resInfo)
        return


################Create all of the mixins##########################
class AndSinglePredicate(SinglePredicate,AndMixin):
    pass
class OrSinglePredicate(SinglePredicate,OrMixin):
    pass
class NotSinglePredicate(SinglePredicate,NotMixin):
    pass
class AndDualPredicate(DualPredicate,AndMixin):
    pass
class OrDualPredicate(DualPredicate,OrMixin):
    pass
class NotDualPredicate(DualPredicate,NotMixin):
    pass

