#####################################################################
#####################################################################
## Copyright 2005 Steven R. Newcomb                                ##
##                                                                 ##
## This software is licensed under the Apache License, Version 2.0 ##
## (the "License"); you may not use this file except in compliance ##
## with the License.  Copies of the License are available at       ##
##        http://www.apache.org/licenses/LICENSE-2.0               ##
##                                                                 ##
## Unless required by applicable law or agreed to in writing,      ##
## software distributed under the License is distributed on an "AS ##
## IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either ##
## express or implied.  See the License for the specific language  ##
## governing permissions and limitations under the License.        ##
##                                                                 ##
## Copies of the source code and the license are available at      ##
##        http://www.versavant.org                                 ##
#####################################################################
#####################################################################

####################################
cvsRevision = "$Revision: 1.2 $" ##
####################################

import sys, re, copy, traceback

propNameParseREString = '(^x_)([RL]r[np])([RL]r[np])([RL]r[np]$)'
propNameParseRE = re.compile( propNameParseREString)


###########################
## U_assert properties implementation
###########################

#######################################################
def Add_U_assert_PropToOverheadDict( vsv, propertyInstance, overheadDict, **args):
    levelAtWhichProxyReferenceAppears = args[ 'levelAtWhichProxyReferenceAppears'] ## 'first', 'second', or 'third'

    ## calculate hashable value, which will be used later by ValuesOf__PropertyInstancesAreSignificantlyDifferent()
    ## (The hashable value will not be used in the overhead dictionary because, since none of the U_assert properties
    ## are SIPs, we don't need the overhead dictionary for determining sameness.)
    propertyInstance.hashablePropertyValue = hashable_U_assert_PropertyValue( vsv, propertyInstance._value)

    ## In order to support the
    ## "infer_U_assert_properties_from_U_assert_properties" conferral
    ## rule, all we need is a list of proxyIds for each U_assert
    ## property class.  But we use only one key in the overheadDict for that purpose: 'proxyIds'
    if overheadDict.has_key( 'proxyIds'):
        if propertyInstance.proxyId not in overheadDict[ 'proxyIds']:
            overheadDict[ 'proxyIds'][ propertyInstance.proxyId] = None
    else:
        overheadDict[ 'proxyIds'] = { propertyInstance.proxyId: None}


    ## register the references to proxies
    workDict = {}
    dict1 = propertyInstance._value
    for dict1Key in dict1:
        if levelAtWhichProxyReferenceAppears == 'first':
            workDict[ dict1Key] = None
            continue
        dict2 = dict1[ dict1Key]
        for dict2Key in dict2:
            if levelAtWhichProxyReferenceAppears == 'second':
                workDict[ dict2Key] = None
                continue
            stringList = dict2[ dict2Key]
            for string in stringList:
                workDict[ string] = None  ## levelAtWhichProxyReferenceAppears == 'third'
    propertyInstance.listOfIdsOfReferencedProxies = workDict.keys()

    vsv.addNewProxyReferencesToReferencedProxiesDict( propertyInstance)

#######################################################
def Remove_U_assert_PropFromOverheadDict( vsv, propertyInstance, overheadDict):
    proxyIdDict = overheadDict[ 'proxyIds']
    if proxyIdDict.has_key( propertyInstance.proxyId):
        del proxyIdDict[ propertyInstance.proxyId]
    else:
        vsv.errMsg( 'No entry in overheadDict of property class for the proxyId (%s) of property %s' % (
            propertyInstance.proxyId,
            vsv.catKey( propertyInstance.topicMap.topicMapName, propertyInstance.propertyInstanceKey)))
        sys.exit( 1)

    vsv.removeOldProxyReferencesFromReferencedProxiesDict( propertyInstance)

#######################################################
def ValueIsInvalidFor_U_assert_Property( vsv, propertyValue, propertyClass, propertyInstance = None, **args):
    TMAName = propertyClass.TMA.TMAName
    topicMap = propertyClass.topicMap

    propertyClassName = propertyClass.propertyClassName

    matchObj = propNameParseRE.match( propertyClassName)
    dict1Code = matchObj.group(2)       ## 'Lrn', 'Rrn', or 'Rrp'
    dict2Code = matchObj.group(3)       ## 'Lrn', 'Rrn', or 'Rrp'
    stringListCode = matchObj.group(4)  ## 'Lrn', 'Rrn', or 'Rrp'

    ## The property class name must be one of:
    ## (1) x_LrnRrnRrp
    ## (2) x_LrnRrpRrn
    ## (3) x_RrnRrpLrn
    ## (4) x_RrnLrnRrp
    ## (5) x_RrpLrnRrn
    ## (6) x_RrpRrnLrn

    if vsv.currentProcOperation:  ## can't do these checks if we're in the middle of anything
        return False

    localRolePlayerProxy = propertyInstance.proxy
    localRolePlayerProxyId = localRolePlayerProxy.proxyId
    dict1 = propertyValue
    for dict1Key in dict1:
        localRoleName = remoteRoleName = remoteRolePlayerProxyId = None
        dict2 = dict1[ dict1Key]
        if dict1Code == 'Lrn':
            localRoleName = dict1Key
        elif dict1Code == 'Rrn':
            remoteRoleName = dict1Key
        else: ## dict1Code == 'Rrp':
            remoteRolePlayerProxyId = dict1Key
        for dict2Key in dict2:
            stringList = dict2[ dict2Key]
            if dict2Code == 'Lrn':
                localRoleName = dict2Key
            elif dict2Code == 'Rrn':
                remoteRoleName = dict2Key
            else: ## dict2Code == 'Rrp':
                remoteRolePlayerProxyId = dict2Key
            for string in stringList:
                if stringListCode == 'Lrn':
                    localRoleName = string
                elif stringListCode == 'Rrn':
                    remoteRoleName = string
                else: ## stringListCode == 'Rrp':
                    remoteRolePlayerProxyId = string

                if localRolePlayerProxy.forwardTo:
                    vsv.errMsg( 'property %s is in a dead proxy!' % ( propertyInstance.propertyInstanceKey))
                    sys.exit( 1)

                remoteRolePlayerProxy = vsv.isProxyId( remoteRolePlayerProxyId)                    
                if remoteRolePlayerProxy.forwardTo:
                    vsv.errMsg( 'remoteRolePlayerProxy %s referenced by %s ("%s") is a dead proxy!' % ( remoteRolePlayerProxy.proxyId, propertyInstance.propertyInstanceKey, propertyInstance._value))
                    sys.exit( 1)
                    

                ## check properties of this proxy
                checkUAssertPropertiesInProxy( vsv, topicMap, TMAName, localRolePlayerProxyId, localRoleName, remoteRolePlayerProxyId, remoteRoleName)

                ## check properties of remote proxy
                checkUAssertPropertiesInProxy( vsv, topicMap, TMAName, remoteRolePlayerProxyId, remoteRoleName, localRolePlayerProxyId, localRoleName)
   


#######################################################
def checkUAssertPropertiesInProxy( vsv, topicMap, TMAName, localProxyId, localRoleName, remoteProxyId, remoteRoleName):
    localProxy = topicMap.proxies[ localProxyId]
    uaPropDict = {
        'x_LrnRrnRrp': None,
        'x_LrnRrpRrn': None,
        'x_RrnRrpLrn': None,
        'x_RrnLrnRrp': None,
        'x_RrpLrnRrn': None,
        'x_RrpRrnLrn': None
    }
    for xPropertyInstanceKey in localProxy.propertyInstances:
        xPropertyInstance = localProxy.propertyInstances[ xPropertyInstanceKey]
        xPropertyClassName = xPropertyInstance.propertyClass.propertyClassName
        if xPropertyClassName in uaPropDict:
            uaPropDict[ xPropertyClassName] = xPropertyInstance

    for uaPropName in uaPropDict:
        uaPropertyInstance = uaPropDict[ uaPropName]
        if uaPropertyInstance == None:
            traceback.print_stack()
            vsv.errMsg( 'No %s property was found in proxy %s.' % ( vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
            vsv.inspect( proxyId = localProxy.proxyId)
            sys.exit( 1)
        uaPropVal = uaPropertyInstance._value                        
        if uaPropName == 'x_LrnRrnRrp':
            if localRoleName not in uaPropVal:
                vsv.errMsg( 'When checking property %s, localRoleName "%s" was not found in %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), localRoleName, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
                sys.exit( 1)
            if remoteRoleName not in uaPropVal[ localRoleName]:
                vsv.errMsg( 'When checking property %s, remoteRoleName "%s" was not found in the [ \'%s\'] dictionary of the %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), remoteRoleName, localRoleName, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
                sys.exit( 1)
            if remoteProxyId not in uaPropVal[ localRoleName][ remoteRoleName]:
                vsv.errMsg( 'When checking property %s, remoteProxyId "%s" was not found in the [ \'%s\'][ \'%s\'] list of the %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), remoteProxyId, remoteRoleName, localRoleName, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
                sys.exit( 1)

        elif uaPropName == 'x_LrnRrpRrn':
            if localRoleName not in uaPropVal:
                vsv.errMsg( 'When checking property %s, localRoleName "%s" was not found in %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), localRoleName, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
                sys.exit( 1)
            if remoteProxyId not in uaPropVal[ localRoleName]:
                vsv.errMsg( 'When checking property %s, remoteProxyId "%s" was not found in the [ \'%s\'] dictionary of the %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), remoteProxyId, localRoleName, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
                sys.exit( 1)
            if remoteRoleName not in uaPropVal[ localRoleName][ remoteProxyId]:
                vsv.errMsg( 'When checking property %s, remoteRoleName "%s" was not found in the [ \'%s\'][ \'%s\'] list of the %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), remoteRoleName, remoteProxyId, localRoleName, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
                sys.exit( 1)

        elif uaPropName == 'x_RrnRrpLrn':
            if remoteRoleName not in uaPropVal:
                vsv.errMsg( 'When checking property %s, remoteRoleName "%s" was not found in %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), remoteRoleName, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
                sys.exit( 1)
            if remoteProxyId not in uaPropVal[ remoteRoleName]:
                vsv.errMsg( 'When checking property %s, remoteProxyId "%s" was not found in the [ \'%s\'] dictionary of the %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), remoteProxyId, remoteRoleName, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
                sys.exit( 1)
            if localRoleName not in uaPropVal[ remoteRoleName][ remoteProxyId]:
                vsv.errMsg( 'When checking property %s, localRoleName "%s" was not found in the [ \'%s\'][ \'%s\'] list of the %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), localRoleName, remoteRoleName, remoteProxyId, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))

        elif uaPropName == 'x_RrnLrnRrp':
            if remoteRoleName not in uaPropVal:
                vsv.errMsg( 'When checking property %s, remoteRoleName "%s" was not found in %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), remoteRoleName, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
                sys.exit( 1)
            if localRoleName not in uaPropVal[ remoteRoleName]:
                vsv.errMsg( 'When checking property %s, localRoleName "%s" was not found in the [ \'%s\'] dictionary of the %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), localRoleName, remoteRoleName, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
                sys.exit( 1)
            if remoteProxyId not in uaPropVal[ remoteRoleName][ localRoleName]:
                vsv.errMsg( 'When checking property %s, remoteProxyId "%s" was not found in the [ \'%s\'][ \'%s\'] list of the %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), remoteProxyId, remoteRoleName, localRoleName, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))

        elif uaPropName == 'x_RrpLrnRrn':
            if remoteProxyId not in uaPropVal:
                vsv.errMsg( 'When checking property %s, remoteProxyId "%s" was not found in %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), remoteProxyId, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
                sys.exit( 1)
            if localRoleName not in uaPropVal[ remoteProxyId]:
                vsv.errMsg( 'When checking property %s, localRoleName "%s" was not found in the [ \'%s\'] dictionary of the %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), localRoleName, remoteProxyId, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
                sys.exit( 1)
            if remoteRoleName not in uaPropVal[ remoteProxyId][ localRoleName]:
                vsv.errMsg( 'When checking property %s, remoteRoleName "%s" was not found in the [ \'%s\'][ \'%s\'] list of the %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), remoteRoleName, remoteProxyId, localRoleName, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))

        else: ## uaPropName == 'x_RrpRrnLrn':
            if remoteProxyId not in uaPropVal:
                vsv.errMsg( 'When checking property %s, remoteProxyId "%s" was not found in %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), remoteProxyId, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
                sys.exit( 1)
            if remoteRoleName not in uaPropVal[ remoteProxyId]:
                vsv.errMsg( 'When checking property %s, remoteRoleName "%s" was not found in the [ \'%s\'] dictionary of the %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), remoteRoleName, remoteProxyId, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))
                sys.exit( 1)
            if localRoleName not in uaPropVal[ remoteProxyId][ remoteRoleName]:
                vsv.errMsg( 'When checking property %s, localRoleName "%s" was not found in the [ \'%s\'][ \'%s\'] list of the %s property of proxy %s.' % ( vsv.catKey( topicMap.topicMapName, TMAName, uaPropertyInstance.propertyInstanceKey), localRoleName, remoteProxyId, remoteRoleName, vsv.catKey( TMAName, uaPropName), localProxy.proxyId))

#######################################################
def Merge_U_assert_PropertyValues( vsv, val1dict1, val2dict1, **args):

    newDict1 = {}

    for dictNum in range( 1, 3):  ## weird, eh?  range( 1, 3) == [ 1, 2]
        if dictNum == 1:
            dict1 = val1dict1
        else:
            dict1 = val2dict1

        for dict1Key in dict1:

            dict2 = dict1[ dict1Key]

            if not newDict1.has_key( dict1Key):
                newDict2 = newDict1[ dict1Key] = {}
            else:
                newDict2 = newDict1[ dict1Key]

            for dict2Key in dict2:

                stringList = dict2[ dict2Key]

                if not newDict2.has_key( dict2Key):
                    newStringList = newDict2[ dict2Key] = []
                else:
                    newStringList = newDict2[ dict2Key]

                for string in stringList:
                    if string not in newStringList:
                        newStringList.append( string)
                

    return newDict1


#######################################################
def ValuesOf_U_assert_PropertyInstancesAreSignificantlyDifferent( vsv, value1, value2, **args):

    propertyInstance1 = args[ 'propertyInstance1']
    propertyInstance2 = args[ 'propertyInstance2']

    hashableValue1 = propertyInstance1.hashablePropertyValue
    if propertyInstance2:
        hashableValue2 = propertyInstance2.hashablePropertyValue
    else:
        hashableValue2 = hashable_U_assert_PropertyValue( vsv, value2)

    if hashableValue1 != hashableValue2:
        return True
    return False


#######################################################
def UpdateProxyReferencesIn_U_assert_Value( vsv, propertyInstance, proxyIdToReplace, replacementProxyId, **args):

    levelAtWhichProxyReferenceAppears = args[ 'levelAtWhichProxyReferenceAppears'] ## 'first', 'second', or 'third'
    foundProxyIdToReplace = False
    dict1 = propertyInstance._value
    newDict1 = {}
    workDict = {}  ## make a list of proxyIds here


    for dict1Key in dict1:

        dict2 = dict1[ dict1Key]

        if levelAtWhichProxyReferenceAppears == 'first':
            if dict1Key == proxyIdToReplace:
                newDict1Key = replacementProxyId
                foundProxyIdToReplace = True
            else:
                newDict1Key = dict1Key
            workDict[ newDict1Key] = None
        else:
            newDict1Key = dict1Key


        if newDict1Key not in newDict1:
            newDict2 = newDict1[ newDict1Key] = {}
        else:
            newDict2 = newDict1[ newDict1Key]

        for dict2Key in dict2:

            stringList = dict2[ dict2Key]

            if levelAtWhichProxyReferenceAppears == 'second':
                if dict2Key == proxyIdToReplace:
                    newDict2Key = replacementProxyId
                    foundProxyIdToReplace = True
                else:
                    newDict2Key = dict2Key
                workDict[ newDict2Key] = None
            else:
                newDict2Key = dict2Key


            if newDict2Key not in newDict2:
                newStringList = newDict2[ newDict2Key] = []
            else:
                newStringList = newDict2[ newDict2Key]

            for string in stringList:

                if levelAtWhichProxyReferenceAppears == 'third':
                    if string == proxyIdToReplace:
                        newString = replacementProxyId
                        foundProxyIdToReplace = True
                    else:
                        newString = string
                    workDict[ newString] = None
                else:
                    newString = string

                if newString not in newStringList:
                    newStringList.append( newString)


    if not foundProxyIdToReplace:
        vsv.errMsg( 'Did not find reference to proxy %s in propertyInstance %s.' % ( proxyIdToReplace, propertyInstance.propertyInstanceKey))
        sys.exit( 1)

    propertyClass = propertyInstance.propertyClass

    ## Remove old value from the "overhead" data for this property class
    if propertyClass.Remove__PropFromOverheadDictFArgString:
        execStr = 'propertyClass.Remove__PropFromOverheadDictF( vsv, propertyInstance, propertyClass.overheadDict, %s)' % (
            propertyClass.Remove__PropFromOverheadDictFArgString)
        exec execStr
    else:
        propertyClass.Remove__PropFromOverheadDictF( vsv, propertyInstance, propertyClass.overheadDict)

    ## Replace the old value with the new one
    dict1.clear()
    dict1.update( newDict1)
        
    ## Expose the list of proxy references that appear in the value as a list of proxyIds
    propertyInstance.listOfIdsOfReferencedProxies = workDict.keys()

###
##     if '00556' in propertyInstance.listOfIdsOfReferencedProxies:
##         vsv.errMsg( '\n%s: %s' % ( propertyInstance.propertyInstanceKey, propertyInstance.listOfIdsOfReferencedProxies))
##         if propertyInstance.topicMap.proxies[ '00556'].forwardTo:
##             traceback.print_stack()
##             vsv.errMsg( '00556 is dead; proxyIdToReplace == "%s", replacementProxyId == "%s"; propertyInstance = %s' % ( proxyIdToReplace, replacementProxyId, propertyInstance.propertyInstanceKey))
##             sys.exit( 1)
###    

    ## Add new value to the "overhead" data for this property class  (This will set propertyInstance.hashableValue, too!)
    if propertyClass.Add__PropToOverheadDictFArgString:
        execStr = 'propertyClass.Add__PropToOverheadDictF( vsv, propertyInstance, propertyClass.overheadDict, %s)' % (
            propertyClass.Add__PropToOverheadDictFArgString)
        exec execStr
    else:
        propertyClass.Add__PropToOverheadDictF( vsv, propertyInstance, propertyClass.overheadDict)


#######################################################
def hashable_U_assert_PropertyValue( vsv, dict1):
    ## Calculate the hashable value of a U_assert property value.
    ## helper function for Add_U_assert_PropToOverheadDict() and
    ## ValuesOf_U_assert_PropertyInstancesAreSignificantlyDifferent().

    dict1List = []
    for dict1Key in dict1:

        dict2 = dict1[ dict1Key]
        dict2List = []
        for dict2Key in dict2:
            dict2List.append( tuple( [ dict2Key, frozenset( dict2[ dict2Key])]))

        dict1List.append( tuple( [ dict1Key, tuple( dict2List)]))
    return tuple( dict1List)
    ## return a tuple of key/value duples in each of which the second member is a
    ## tuple of duples in each of which the second member is a frozenset.

            
