#!/usr/bin/env python
#
import coral
TM = coral.getTransactionManager()
import re
from coral.modeler.diagram.degen import DiagramElementGenerator as DEG
from coral.modeler import modeler,action
import coral.modeler.diagram.xmidi_diagrams as diagrams

m=modeler.ModelerWindow()

m.main(loop=0)

UML = coral.lang.UML14
DIML = coral.lang.DIML10
DI = coral.lang.XMIDI20

def __comparefunction(x,y):
    if x.name>y.name:
        return 1
    return -1

def __otherEnd(associationend):
    association = associationend.association
    idx = 0
    if association.connection[0] == associationend:
        idx = 1
    return association.connection[idx]

def __langProperty2SMDProperty(property):
    element = property.getOwner().getCorrespondingElement()
    p = element.properties.select(lambda x: x.name == property.getName())
    if p:
        return p[0]

def __getChildLinks(node):
    """ This method lists the links from  a class and returns them sorted."""
    seq = [i for i in node.association if i.aggregation == "composite"]
    seq.sort(lambda x,y: cmp(x.association.name, y.association.name))
    return seq

def __getChild(ass_end):
    return __otherEnd(ass_end).participant


def __stringToED(definition):

    m=re.search("(\w+)(\d)(\d)::\s*(.*)",str(definition))
    assert m
    lang = m.group(1)
    ver = m.group(2)
    rev = m.group(3)
    classname=m.group(4)

    L = coral.Language.__getLanguage__(lang, int(ver), int(rev))
    

    ed = L.getElement(classname)

    assert ed

    return ed, L


def __makeSelectionGuard(association, contained, metaclass, language):

    property = ""
    constraint = ""
    childconstraint = ""
    retrieve = ""

    for tv in association.taggedValue:
        m = re.search("^constraint:\s*(.*)", str(tv.dataValue))
        if m:
            constraint = str(m.group(1))

        m = re.search("property:\s*(.*)", str(tv.dataValue))
        if m:
            property = str(m.group(1))

        m = re.search("retrieve:\s*(.*)", str(tv.dataValue))
        if m:
            retrieve = str(m.group(1))

        m = re.search("childconstraint:\s*(.*)", str(tv.dataValue))
        if m:
            childconstraint = str(m.group(1))

    s = DIML.Selection()
    create_sel = False
    
    if property:
        p = metaclass.getPropertyByName(property)
        
        s.property = __langProperty2SMDProperty(p)

        create_sel = True    

    g = DIML.Guard()
    create_grd = False
    if constraint:
        g.expression = unicode(constraint)
        create_grd = True

    stype = None

    if childconstraint:
        m = re.search("child\.oclIsKindOf\((.*)\)", childconstraint)
        if m:
            
            s.type = language.getElement(str(m.group(1))).getCorrespondingElement()
            stype = language.getElement(str(m.group(1)))
            create_sel = True

    if retrieve:
        create_sel = True
        m1 = re.search("element\.(.*)\.select\(lambda\s*x\s*\:\s*x\.([^\s]*)\s*([!=]*)\s*\"(.*)\"\)", retrieve)
        m2 = re.search("element\.(.*)\.select\(lambda\s*x\s*\:\s*x\.oclIsKindOf\((.*)\)\)", retrieve)

        if m1 and not m2:
            prop = metaclass.getPropertyByName(unicode(m1.group(1)))
            if s.type:
                pt = stype.getPropertyByName(unicode(m1.group(2)))
                if pt:
                    s.parameterType =  __langProperty2SMDProperty(pt)
            else:
                s.type = prop.getTarget().getCorrespondingElement()
                stype = prop.getTarget()
                pt = stype.getPropertyByName(unicode(m1.group(2)))
                if pt:
                    s.parameterType = __langProperty2SMDProperty(pt)


            opk = unicode(m1.group(3))
            if opk:
                s.operation = opk

            pv = unicode(m1.group(4))
            if pv:
                s.parameterValue = pv

        if m2 and not m1:
            if not s.type:
                s.type = language.getElement(unicode(m2.group(2))).getCorrespondingElement()

    if s.type and not (s.parameterType or s.parameterValue):
        if s.type == s.property.element_kind:
            print "Reduced property "+unicode(s.property.name)+ " element_kind "+unicode(s.type.name)+" !"
            s.type = None

    TM.beginModifyCmd()
    try:
        if create_sel:
            contained.selection = s
        if create_grd:
            contained.guard = g
    finally:
        TM.endModifyCmd()


def __makeContextGuard(dep, mapping):

    guard = ""
    g = DIML.Guard()
    for tv in dep.taggedValue:
        m = re.search("^guard:\s*(.*)", str(tv.dataValue))
        if m:
            guard = str(m.group(1))
            
    
    if guard:
        g.expression = guard
        TM.beginModifyCmd()
        try:
            mapping.contextGuard = g
        finally:
            TM.endModifyCmd()


def __readContextProps(dep, mapping):

    cli = dep.client[0]
    minor_el = ""
    sd = ""
    if cli:
        for f in cli.feature:
            if str(f.name) == "isMinorElement":
                minor_el = unicode(f.initialValue.body)
            if str(f.name) == "suitableDiagrams":
                sd = unicode(f.initialValue.body)

    if sd:
        sd = sd[1:-1]
        sd = sd.replace('"','')
        sd = sd.replace(',',' ')
        l = sd.split()
        for i in l:
            a = coral.String.newAtom()
            a.__setString__(i)
            mapping.suitableDiagrams.append(a)

    if minor_el:
        mapping.isMinorElement = minor_el.lower()
        


def __makeConnectors(node, gep, metaclass):


    properties = []
    if str(node.name) == "GraphEdge":
        for tv in node.taggedValue:
            m = re.search("index1:\s*(.*)\s", str(tv.dataValue))
            n = re.search("index2:\s*(.*)\s", str(tv.dataValue))
            if m:
                m2 = re.search("\[\s*element\.(.*)\s*\,\s*element\.(.*)\]", str(m.group(1)))
                if m2:
                    properties.append(str(m2.group(1)))
                    properties.append(str(m2.group(2)))
                else:
                    m3 = re.search("element\.(.*)", str(m.group(1)))
                    if m3 and n:
                        n2 = re.search("element\.(.*)", str(n.group(1)))
                        if n2:
                            properties.append(str(m3.group(1)))
                            properties.append(str(n2.group(1)))

    for p in properties:
        c = DIML.Connector()
        prop = metaclass.getPropertyByName(p)
        if prop:
            c.property = __langProperty2SMDProperty(prop)
        gep.connector.append(c)


def __giveAppropriateElement(umlelement, metaclass, language):

    if umlelement.oclIsKindOf(UML.Class):
        if unicode(umlelement.name) == "GraphNode":
            new_el = DIML.GraphNodePart()
            for f in umlelement.feature:
                if str(f.name) == "noXMI":
                    new_el.skipXMIDICreation = "true"
                    break
            return new_el
        elif unicode(umlelement.name) == "GraphEdge":
            new_el = DIML.GraphEdgePart()
            __makeConnectors(umlelement, new_el, metaclass)
            return new_el
        elif unicode(umlelement.name) == "?":
            new_el = DIML.Delegation()
            return new_el
        elif unicode(umlelement.name) == "Diagram":
            new_el = DIML.DiagramPart()
            return new_el
        else:
            new_el = DIML.GraphNodePart()
            new_el.typeInfo = unicode(umlelement.name)
            return new_el
    elif umlelement.oclIsKindOf(UML.Dependency):
        if unicode(umlelement.name) == "separator":
            new_el = DIML.Separator()
            return new_el
        else:
            new_el = DIML.ElementToXMIDIMapping()
            __makeContextGuard(umlelement, new_el)
            __readContextProps(umlelement, new_el)
            return new_el
            
    elif umlelement.oclIsKindOf(UML.Association):
        new_el = DIML.Contained()
        __makeSelectionGuard(umlelement, new_el, metaclass, language)
        return new_el

thex, they = 0,0

def convertModel(filename):
    global thex, they
    umlclasses=[]

    st = coral.loadDocument(filename)
    model = st.contents().select(lambda x: x.__type__().__language__() == UML)[0]

    umlclasses = model.__getAllParts__().select(lambda x: x.__type__() == UML.Class and len(x.clientDependency) and len(x.taggedValue) == 1 and re.search("^init\s*$", str(x.taggedValue[0].dataValue)))

    listOfClasses =[]
    for umlclass in umlclasses:
        listOfClasses.append(umlclass)
    listOfClasses.sort(__comparefunction)

    prj = action.newProject(("DIML",1,0))
    mm = prj.contents()[0]
    TM.beginModifyCmd()
    try:
        for umlclass in listOfClasses:
            metaclass, language = __stringToED(unicode(umlclass.name))
            deps = umlclass.clientDependency
            diagram = diagrams.createDiagram(prj, typeinfo="MappingDiagram", owner_elem = mm)

            diagram.name = unicode(umlclass.name) + " Creation"
            diagram.size = DI.Dimension(width=2000, height=2000)
            m.getDiagramEditorManager().editorSet.notifyFocusOn(diagram)
            ext = DEG.getExtension(metaclass.getCorrespondingElement().__type__())
            mcls_xmidi = ext.createDiagramElement(metaclass.getCorrespondingElement(), xparent=diagram, container=diagram)
            for d in deps:
                e2xmidi = __giveAppropriateElement(d, metaclass, language)
                mm.ownedElement.append(e2xmidi)
                
                e2xmidi.element = metaclass.getCorrespondingElement()
                ext = DEG.getExtension(e2xmidi.__type__())
                map_xmidi = ext.createDiagramElement(e2xmidi, xparent=diagram, container=diagram)
                gc1 = DI.GraphConnector(graphElement = mcls_xmidi)
                map_xmidi.anchor.append(gc1)
                suppnode = d.supplier[0]
                TM.beginModifyCmd()
                try:
                    thex, they = 0,0
                    gen_diml(suppnode, metaclass, language, diagram, mm, contained=None, mapping=e2xmidi)
                finally:
                    TM.endModifyCmd()    
    finally:
        TM.endModifyCmd()    


    prj.setFilename(filename+".DIML.xml")
    coral.saveDocument(prj)

def gen_diml(node, metaclass, language, diagram, model, contained=None, mapping=None):
    global thex, they
    element = __giveAppropriateElement(node, metaclass, language)

    model.ownedElement.insert(element)
    
    ext = DEG.getExtension(element.__type__())

    assert ext
    
    xmidi_element = ext.createDiagramElement(element, xparent=diagram, container=diagram, position=DI.Point(x=thex, y=they))

    if contained:
        xmidi_contained = contained.xmidi_get_graphelements()[0]
        contained.child = element
        gc2 = DI.GraphConnector(graphElement = xmidi_element)
        xmidi_contained.anchor.append(gc2)

    if mapping:
        xmidi_mapping = mapping.xmidi_get_graphelements()[0]
        mapping.root = element
        gc1 = DI.GraphConnector(graphElement = xmidi_element)
        xmidi_mapping.anchor.append(gc1)
    # The associations tell us what child elements the GraphElement should have 
    association_ends = __getChildLinks(node)
    been = False
    i  = 0
    for ae in association_ends:
        i += 1
        if not been:
            thex = thex+50
            they = they+80
            been = True

        thex+=100
        association = ae.association

        cntd = __giveAppropriateElement(association, metaclass, language)
        model.ownedElement.append(cntd)
        
        ext = DEG.getExtension(cntd.__type__())
        assert ext
        xmidi_cntd = ext.createDiagramElement(cntd, xparent=diagram, container=diagram)


        child = __getChild(ae)
        #child_el = __giveAppropriateElement(child, metaclass, language)

        element.children.append(cntd)

        gc1 = DI.GraphConnector(graphElement = xmidi_element)

        xmidi_cntd.anchor.append(gc1)

        gen_diml(child, metaclass, language, diagram, model, contained=cntd)

        if i == len(association_ends)-1:
            thex = 0

def main(arguments):
    if arguments:
        TM.beginModifyCmd()
        try:
            for a in arguments:
                convertModel(a)
        finally:
            TM.endModifyCmd()    


if __name__=='__main__':
    import sys
    main(sys.argv[1:])
