# This file is part of the Coral project.
# Please read the copyright notice in the LICENSE file.

# This file copied from SMW (mmgen/UML14/UML14actions.py)

import string
from qt import *
import coral
coral.lang.UML14.__import__(globals())
    
def newMultiplicity(lower=1,upper=1):
    m=Multiplicity()
    m.range.insert(MultiplicityRange(lower=lower,upper=upper))
    return m

def newAssociation(classifier1,classifier2,name=None):
    if not name:
        name=''
    as=Association(name=name,namespace=classifier1.namespace)
    ae1=AssociationEnd(participant=classifier1,
                       association=as,
                       multiplicity=newMultiplicity(1,1),
                       isNavigable=1)
        
    ae2=AssociationEnd(participant=classifier2,
                       association=as,
                       multiplicity=newMultiplicity(1,1),
                       isNavigable=1)

    return as

def newAssociationRole(classifierrole1,classifierrole2,name=None):
    if not name:
        name='unnamed'
    baseAss=classifierrole1.base[0].association[0].association #FIXME
    asr=AssociationRole(base=baseAss,name=name,namespace=classifierrole1.namespace)
    aer1=AssociationEndRole(association=asr,
                            participant=classifierrole1,
                            base=baseAss.connection[0],
                            multiplicity=newMultiplicity(1,1),
                            isNavigable=1)
    aer2=AssociationEndRole(association=asr,
                            participant=classifierrole2,
                            base=baseAss.connection[1],
                            multiplicity=newMultiplicity(1,1),
                            isNavigable=1)

    return asr
def collectAssociations(clf,assoc=None):
    """Method for collecting all AssociationEnds from a classifier and all
    its superclasses.
    """
    if not assoc:
        assoc=[]
    assoc+=clf.association
    for g in clf.generalization:
        assoc+=g.parent.association
        if g.parent.generalization:
            return collectAssociations(g.parent,assoc)
    else:
        return assoc

def newLink(instance1,instance2,name=None):
    """Creates a new link between two instances, there must exist an
    association between the base classifiers (or superclasses of these)
    for the link to map onto, otherwise it will return None and no Link
    is created.
    returns the new Link
    """
    if not name:
        name='unnamed'
    baseAss=None

    assocs1=MMSet()
    assocs2=MMSet()

    assocs1=collectAssociations(instance1.classifier[0])
    assocs2=collectAssociations(instance2.classifier[0])
    for a in assocs1:
        for b in assocs2:
            if not a == b and a.association == b.association:
                baseAss=a.association
                break

    if not baseAss:
        return None
    
    link=Link(name=name,association=baseAss,namespace=instance1.namespace)

    if baseAss.connection[0].participant == instance1.classifier[0]:
        le1=LinkEnd(instance=instance1,link=link,
                    associationEnd=baseAss.connection[0])
        le2=LinkEnd(instance=instance2,link=link,
                    associationEnd=baseAss.connection[1])
    else:
        le1=LinkEnd(instance=instance1,link=link,
                    associationEnd=baseAss.connection[1])
        le2=LinkEnd(instance=instance2,link=link,
                    associationEnd=baseAss.connection[0])
    
    return link

def fromMultiplicityRangeToString(mrange):
    lower = mrange.lower
    upper = mrange.upper

    if upper == -1:
        upper = "*"

    if lower==0 and upper=="*":
        return "*"

    if str(lower)==str(upper):
        return str(lower)

    return str(lower) + ".." + str(upper)
    
    
def fromMultiplicityToString(multiplicity):
    """
    fromMultiplicityToString creates a string from a multiplicity instance that
    will be displayed in the editor, i.e. 2..7 .

    Example: 
      testlist='2..7'
      a=fromStringToMultiplicity(testlist)
      b=fromMultiplicityToString(a)
      assert('2..7'==b)
    """
    
    if not multiplicity:
        return ""
    
    m = [ r for r in multiplicity.range ]
    m.sort(lambda x, y: cmp(x.lower, y.lower))

    l = [ fromMultiplicityRangeToString(i) for i in m ]

    return ", ".join(l)


def fromStringToMultiplicity(s):

    """
    fromStringToMultiplicity creates an Multiplicity instance from
    a given string containing a multiplicity range such as '1..5', '*' or
    '2..5,7..10'

    Example:
     testlist='2..  7,  10.. 29, 23..2'
     m=fromStringToMultiplicity(testlist)
     assert(m.range[0].lower==2 and m.range[0].upper==7)
     assert(m.range[1].lower==10 and m.range[1].upper==29)
     assert(m.range[2].lower==2 and m.range[2].upper==23)
     
    It returns None if the string is empty or contains an invalid
    multiplicty range.    
    """

    s=string.replace(s," ","")
    list=string.split(s,",")

    if len(list)==0:
        return None

    m=Multiplicity()

    for temp in list:                             
        r=string.split(temp,"..")
 
        #If only  element x in list, lower=x and upper=x
        if len(r)==1:     
            r.append(r[0])

       # r[0]=string.strip(r[0])
        if r[0]=='*':
            r[0]=0
            r.append(-1)
        else:
            try:
                r[0]=int(r[0])
            except ValueError:
                return None
      
        if r[1]=='*':
            r[1]=-1
        else:
            try:
                r[1]=int(r[1])
            except ValueError:
                return None

        
        if r[1] != -1 and r[0] > r[1] or r[0]==-1:
            r[0],r[1]=r[1],r[0] #Swap

           
        if len(r)==3 and r[1]!=-1: #if input is i.e(*..5) swap to (5..*)
            r[0]=r[1]
            r[1]=r[2]
        
        m.range.insert(MultiplicityRange(lower=r[0],upper=r[1]))
        #for loop ends
   
        
    #creates a new multiplicity instance with the given multiplicities sorted!    
    return m

def fromAttributeToString(attr):
    """
    fromAttributeToString takes the attributes from the Attribute instance and
    converts them into a string. This string is then used for proper output in the
    class diagram, i.e. ([visibility][name]:[type]=[body]), +x:int=5...

    Example:
    
    attr=Attribute()
    attr.name=\"theAttr\"
    myExp=Expression()
    myExp.body=\"Hello World!\"
    myExp.language=\"\"
    attr.initialValue=myExp
    clfier=Classifier(name=\"String\")
    attr.type=clfier
    attr.visibility=VisibilityKind.vk_protected
    
    assert(fromAttributeToString(attr)==\"#theAttr:String=Hello World!\")
    """
    
    s=""
    if attr.visibility == VisibilityKind.vk_public:
        s=s+"+"
    elif attr.visibility == VisibilityKind.vk_private:
        s=s+"-"
    elif attr.visibility == VisibilityKind.vk_protected:
        s=s+"#"
    elif attr.visibility == VisibilityKind.vk_package:
        s=s+"~"

    if attr.name:
        s=s+attr.name
    if attr.type:
        s=s+":"+attr.type.name
    if attr.initialValue and attr.initialValue.body:
        s=s+"="+attr.initialValue.body
    
    if s=='+':
        return None
    else:
        return s

    

def fromBehavioralFeatureToString(bf):
    '''
    fromBehaviorToString takes the name, parameters, type (and kind), etc. from
    the BehavioralFeature instance for output to be printed in the diagram...

    Example:
    op=Operation(name="add")
    op.parameter.insert(Parameter(name="x",type=t))
    op.parameter.insert(Parameter(kind=ParameterDirectionKind.pdk_return, type=t))
    op.parameter.insert(Parameter(name="y",type=t))
    op.visibility=VisibilityKind.vk_private
    assert(fromBehavioralFeatureToString(op)=="-add(x:int,y:int):int")

    According to specifications if a BehavioralFeature returns nothing (i.e. void)
    the return type should be omitted.
    '''

    
    s=""
    if bf.visibility == VisibilityKind.vk_public:
        s=s+"+"
    elif bf.visibility == VisibilityKind.vk_private:
        s=s+"-"
    elif bf.visibility == VisibilityKind.vk_protected:
        s=s+"#"
    elif bf.visibility == VisibilityKind.vk_package:
        s=s+"~"
    first=1
    end="" #according to "the bible" it should be nothing
    s=s+bf.name+"("
    for p in bf.parameter:
        if p.kind==ParameterDirectionKind.pdk_return and p.type and not p.type.name == "void":            
            end+=":"+p.type.name

        if not first and p.name and p.kind!=ParameterDirectionKind.pdk_return:
            s=s+','

        if p.name and p.kind!=ParameterDirectionKind.pdk_return:       
            s=s+p.name
            first=0

        if p.type and p.type.name!="" and p.name and p.kind!=ParameterDirectionKind.pdk_return:
            s=s+":"+p.type.name
                         
    s=s+")"+end
                
    return s

def fromParameterToString(p):
    '''fromParameterToString returns the textual representation of a parameter according to the specification.

    Example:
    t2=Class(name="String")
    p=Parameter(name="name",type=t2)
    exp=Expression()
    exp.body="Hello World"
    p.kind=ParameterDirectionKind.pdk_inout
    p.defaultValue=exp
    assert(fromParameterToString(p)=="in-out name:String=Hello World")
    '''
    
    s=""
    if p.kind==ParameterDirectionKind.pdk_in:
        s+="in "
    elif p.kind==ParameterDirectionKind.pdk_inout:
        s+="in-out "
    elif p.kind==ParameterDirectionKind.pdk_out:
        s+="out "
    elif p.kind==ParameterDirectionKind.pdk_return:
        s+="return "
    if p.name!="":
        s+=p.name
    if p.type:
        if p.type.name!="":
            s+=":"
        s+=p.type.name
        
    if p.defaultValue and p.defaultValue.body !="":
        s+="="+p.defaultValue.body
        
    return s

def fromOrderingToString(ae):
    """fromOrderingToString gets the orderingkind from an associationEnd so that it will be easier to print relevant output to the screen..."""
    s=""
    if ae.ordering==OrderingKind.ok_ordered:
        return "{ordered}"
    else:
        return s


def fromVisibilityToString(ae):
    """fromVisibilityToString takes an AssociationEnd Rolename as the argument and returns the given visibility of the name e.g +specification=public..."""
    s=""
    
    if ae.visibility == VisibilityKind.vk_public:
        s=s+"+"
    elif ae.visibility == VisibilityKind.vk_private:
        s=s+"-"
    elif ae.visibility == VisibilityKind.vk_protected:
        s=s+"#"
        
    return s

def fromFQPToElement(s,model):
    """fromFQPToElement takes a FullyQualifiedPath in the form of a string and a model and
    returns a reference to that particular element (if it exists).

    If the element does not exist, it returns None.
    """
    if not model:
        return None
    
    names=string.split(s,"::")

    for e in model.ownedElement:
    
        if e.name==names[0] and len(names)==1:
  
            return e
        elif len(names)>1:
  
            if e.name==names[0]:
  
                names=names[1:]
                return fromFQPToElement(string.join(names,"::"),e)
    #out of elements..
    return None

def convertSimpleToCompositeState(state):
        assert(state.oclIsKindOf(SimpleState))
 
        # this is a really dirty hack
        # and it is not undoable
        state.__setclass__(CompositeState)
        state.subvertex=[]
        
        state.isConcurrent=0
        state.isRegion=0

        return state
