#!/usr/bin/env python
import coral
import coral.modeler
import coral.modeler.modeler

TM = coral.getTransactionManager()

## Checking that original model is OK

prj = coral.loadDocument("testOpParamSynch.xml")

class_diagram = prj.contents()[1]
model = prj.contents()[0]

L = coral.lang.UML14
DI = coral.lang.XMIDI20
assert model.oclIsKindOf(L.Model)
assert class_diagram.oclIsKindOf(DI.Diagram)

c1 = model.ownedElement.select(lambda x: x.name == "C1")[0]

assert c1

xc1 = c1.xmidi_get_graphelements()[0]

assert xc1

# Check that we have an operation compartment
opcomp = xc1.contained[4]
assert opcomp
assert opcomp.semanticModel.typeInfo == "OperationCompartment"

# Check we have a delimitedsection within it
delsec = opcomp.contained[0]
assert delsec
assert delsec.semanticModel.typeInfo == "DelimitedSection"

# We start with no operation
ops = c1.feature.select(lambda x: x.oclIsKindOf(L.Operation))
assert len(ops) == 0
assert len(delsec.contained) == 0


# Create an operation
op1 = L.Operation(name="op1")

TM.beginModifyCmd()
try:
    c1.feature.append(op1)
finally:
    TM.endModifyCmd()

# We check that it's synchronized
ops = c1.feature.select(lambda x: x.oclIsKindOf(L.Operation))
assert len(ops) == 1
assert len(delsec.contained) == 1

# Get the GN of the operation
xop1 = delsec.contained[0]
assert xop1
assert xop1.semanticModel.element == op1

# the operation GN contains 5 GN's
assert len(xop1.contained) == 5                                        # +name():
assert xop1.contained[0].semanticModel.typeInfo == "Visibility"        # +
assert xop1.contained[1].semanticModel.typeInfo == "Name"              # name
assert xop1.contained[2].semanticModel.typeInfo == "ParameterStart"    # (
assert xop1.contained[3].semanticModel.typeInfo == "ParameterEnd"      # )
assert xop1.contained[4].semanticModel.typeInfo == "TypeSeparator"     # :


# Create 5 parameters
p1 = L.Parameter(name="p1")
p2 = L.Parameter(name="p2")
p3 = L.Parameter(name="p3")
p4 = L.Parameter(name="p4")
p5 = L.Parameter(name="p5")

# By default a parameter has .kind == "in", so each added parameter should go between the parenthesises

# Add one parameter
TM.beginModifyCmd()
try:
    op1.parameter.append(p1)
finally:
    TM.endModifyCmd()

# Check structure again
# the operation GN contains 6 GN's
assert len(op1.parameter) == 1
assert len(xop1.contained) == 6                                        # +name(p1):
assert xop1.contained[0].semanticModel.typeInfo == "Visibility"        # +
assert xop1.contained[1].semanticModel.typeInfo == "Name"              # name
assert xop1.contained[2].semanticModel.typeInfo == "ParameterStart"    # (
assert xop1.contained[3].semanticModel.element == p1                   # p1
assert xop1.contained[4].semanticModel.typeInfo == "ParameterEnd"      # )
assert xop1.contained[5].semanticModel.typeInfo == "TypeSeparator"     # :

# Add another parameter
TM.beginModifyCmd()
try:
    op1.parameter.append(p2)
finally:
    TM.endModifyCmd()

# Check structure again
# the operation GN contains 8 GN's
assert len(op1.parameter) == 2
assert len(xop1.contained) == 8                                         # +name(p1,p2):
assert xop1.contained[0].semanticModel.typeInfo == "Visibility"         # +
assert xop1.contained[1].semanticModel.typeInfo == "Name"               # name
assert xop1.contained[2].semanticModel.typeInfo == "ParameterStart"     # (
assert xop1.contained[3].semanticModel.element == p1                    # p1
assert xop1.contained[4].semanticModel.typeInfo == "ParameterSeparator" # ,
assert xop1.contained[5].semanticModel.element == p2                    # p2
assert xop1.contained[6].semanticModel.typeInfo == "ParameterEnd"       # )
assert xop1.contained[7].semanticModel.typeInfo == "TypeSeparator"      # :

# Add a third parameter, now between p1 and p2
TM.beginModifyCmd()
try:
    op1.parameter.insert(p3, 1)
finally:
    TM.endModifyCmd()

# Check structure again
# the operation GN contains 10 GN's
assert len(op1.parameter) == 3
assert len(xop1.contained) == 10                                         # +name(p1,p3,p2):
assert xop1.contained[0].semanticModel.typeInfo == "Visibility"          # +
assert xop1.contained[1].semanticModel.typeInfo == "Name"                # name
assert xop1.contained[2].semanticModel.typeInfo == "ParameterStart"      # (
assert xop1.contained[3].semanticModel.element == p1                     # p1
assert xop1.contained[4].semanticModel.typeInfo == "ParameterSeparator"  # ,
assert xop1.contained[5].semanticModel.element == p3                     # p3
assert xop1.contained[6].semanticModel.typeInfo == "ParameterSeparator"  # ,
assert xop1.contained[7].semanticModel.element == p2                     # p2
assert xop1.contained[8].semanticModel.typeInfo == "ParameterEnd"        # )
assert xop1.contained[9].semanticModel.typeInfo == "TypeSeparator"       # :


# Add a fourth parameter, as a return parameter in position 0
# after op1.parameter == [p4, p1, p3, p2]
p4.kind = "return"

TM.beginModifyCmd()
try:
    op1.parameter.insert(p4, 0)
finally:
    TM.endModifyCmd()

# Check structure again
# the operation GN contains 11 GN's
assert len(op1.parameter) == 4
assert len(xop1.contained) == 11                                         # +name(p1,p3,p2):p4
assert xop1.contained[0].semanticModel.typeInfo == "Visibility"          # +
assert xop1.contained[1].semanticModel.typeInfo == "Name"                # name
assert xop1.contained[2].semanticModel.typeInfo == "ParameterStart"      # (
assert xop1.contained[3].semanticModel.element == p1                     # p1
assert xop1.contained[4].semanticModel.typeInfo == "ParameterSeparator"  # ,
assert xop1.contained[5].semanticModel.element == p3                     # p3
assert xop1.contained[6].semanticModel.typeInfo == "ParameterSeparator"  # ,
assert xop1.contained[7].semanticModel.element == p2                     # p2
assert xop1.contained[8].semanticModel.typeInfo == "ParameterEnd"        # )
assert xop1.contained[9].semanticModel.typeInfo == "TypeSeparator"       # :
assert xop1.contained[10].semanticModel.element == p4                    # p4

# Add a fifth parameter, as a return parameter in position 2
# after op1.parameter == [p4, p1, p5, p3, p2]
p5.kind = "return"

TM.beginModifyCmd()
try:
    op1.parameter.insert(p5, 2)
finally:
    TM.endModifyCmd()


# Check structure again
# the operation GN contains 13 GN's
assert len(op1.parameter) == 5
assert len(xop1.contained) == 13                                         # +name(p1,p3,p2):p4, p5
assert xop1.contained[0].semanticModel.typeInfo == "Visibility"          # +
assert xop1.contained[1].semanticModel.typeInfo == "Name"                # name
assert xop1.contained[2].semanticModel.typeInfo == "ParameterStart"      # (
assert xop1.contained[3].semanticModel.element == p1                     # p1
assert xop1.contained[4].semanticModel.typeInfo == "ParameterSeparator"  # ,
assert xop1.contained[5].semanticModel.element == p3                     # p3
assert xop1.contained[6].semanticModel.typeInfo == "ParameterSeparator"  # ,
assert xop1.contained[7].semanticModel.element == p2                     # p2
assert xop1.contained[8].semanticModel.typeInfo == "ParameterEnd"        # )
assert xop1.contained[9].semanticModel.typeInfo == "TypeSeparator"       # :
assert xop1.contained[10].semanticModel.element == p4                    # p4
assert xop1.contained[11].semanticModel.typeInfo == "ParameterSeparator" # ,
assert xop1.contained[12].semanticModel.element == p5                    # p5

# Now assign p1 as a return parameter
p1.kind = "return"

# HACK We can't trigger synch of parameter-slot
# when a property of a contained element changes
assert len(op1.parameter) == 5

TM.beginModifyCmd()
try:
    p = L.Parameter()
    op1.parameter.append(p)
    op1.parameter.remove(p)
finally:
    TM.endModifyCmd()

assert len(op1.parameter) == 5

# Check structure again
# the operation GN contains 13 GN's
assert len(op1.parameter) == 5
assert len(xop1.contained) == 13                                         # +name(p3,p2):p4, p1, p5
assert xop1.contained[0].semanticModel.typeInfo == "Visibility"          # +
assert xop1.contained[1].semanticModel.typeInfo == "Name"                # name
assert xop1.contained[2].semanticModel.typeInfo == "ParameterStart"      # (
assert xop1.contained[3].semanticModel.element == p3                     # p3
assert xop1.contained[4].semanticModel.typeInfo == "ParameterSeparator"  # ,
assert xop1.contained[5].semanticModel.element == p2                     # p2
assert xop1.contained[6].semanticModel.typeInfo == "ParameterEnd"        # )
assert xop1.contained[7].semanticModel.typeInfo == "TypeSeparator"       # :
assert xop1.contained[8].semanticModel.element == p4                     # p4
assert xop1.contained[9].semanticModel.typeInfo == "ParameterSeparator"  # ,
assert xop1.contained[10].semanticModel.element == p1                    # p1
assert xop1.contained[11].semanticModel.typeInfo == "ParameterSeparator" # ,
assert xop1.contained[12].semanticModel.element == p5                    # p5

# Revert the previous
# Now assign p1 as a return parameter
p1.kind = "in"

# HACK We can't trigger synch of parameter-slot
# when a property of a contained element changes
assert len(op1.parameter) == 5

TM.beginModifyCmd()
try:
    p = L.Parameter()
    op1.parameter.append(p)
    op1.parameter.remove(p)
finally:
    TM.endModifyCmd()

assert len(op1.parameter) == 5

# Check structure again
# the operation GN contains 13 GN's
assert len(op1.parameter) == 5
assert len(xop1.contained) == 13                                         # +name(p1,p3,p2):p4, p5
assert xop1.contained[0].semanticModel.typeInfo == "Visibility"          # +
assert xop1.contained[1].semanticModel.typeInfo == "Name"                # name
assert xop1.contained[2].semanticModel.typeInfo == "ParameterStart"      # (
assert xop1.contained[3].semanticModel.element == p1                     # p1
assert xop1.contained[4].semanticModel.typeInfo == "ParameterSeparator"  # ,
assert xop1.contained[5].semanticModel.element == p3                     # p3
assert xop1.contained[6].semanticModel.typeInfo == "ParameterSeparator"  # ,
assert xop1.contained[7].semanticModel.element == p2                     # p2
assert xop1.contained[8].semanticModel.typeInfo == "ParameterEnd"        # )
assert xop1.contained[9].semanticModel.typeInfo == "TypeSeparator"       # :
assert xop1.contained[10].semanticModel.element == p4                    # p4
assert xop1.contained[11].semanticModel.typeInfo == "ParameterSeparator" # ,
assert xop1.contained[12].semanticModel.element == p5                    # p5

# Remove p1 and p4 simultaneously in one transaction
TM.beginModifyCmd()
try:
    op1.parameter.remove(p1)
    op1.parameter.remove(p4)
finally:
    TM.endModifyCmd()

# op1.parameter == [p5, p3, p2]
# Check structure again
# the operation GN contains 13 GN's
assert len(op1.parameter) == 3
assert len(xop1.contained) == 9                                          # +name(p3,p2):p5
assert xop1.contained[0].semanticModel.typeInfo == "Visibility"          # +
assert xop1.contained[1].semanticModel.typeInfo == "Name"                # name
assert xop1.contained[2].semanticModel.typeInfo == "ParameterStart"      # (
assert xop1.contained[3].semanticModel.element == p3                     # p3
assert xop1.contained[4].semanticModel.typeInfo == "ParameterSeparator"  # ,
assert xop1.contained[5].semanticModel.element == p2                     # p2
assert xop1.contained[6].semanticModel.typeInfo == "ParameterEnd"        # )
assert xop1.contained[7].semanticModel.typeInfo == "TypeSeparator"       # :
assert xop1.contained[8].semanticModel.element == p5                     # p5


# Remove the rest simultaneously in one transaction
TM.beginModifyCmd()
try:
    op1.parameter.remove(p3)
    op1.parameter.remove(p2)
    op1.parameter.remove(p5)
finally:
    TM.endModifyCmd()

# the operation GN contains 5 GN's
assert len(op1.parameter) == 0
assert len(xop1.contained) == 5                                        # +name():
assert xop1.contained[0].semanticModel.typeInfo == "Visibility"        # +
assert xop1.contained[1].semanticModel.typeInfo == "Name"              # name
assert xop1.contained[2].semanticModel.typeInfo == "ParameterStart"    # (
assert xop1.contained[3].semanticModel.typeInfo == "ParameterEnd"      # )
assert xop1.contained[4].semanticModel.typeInfo == "TypeSeparator"     # :
