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

#
# lang2smd - In-memory Language to SMD model dumper
#
# SYNOPSIS
#
# lang2smd langname version revision xmioutputfile
#
#
# DESCRIPTION
#
# lang2smd loads a language into memory, creates the
# appropriate SMD model representing the in-memory metamodel,
# and dumps it into a file.
#
# The output file can then be loaded by the SMD metamodel
# and the routine smd2lang can be used
# to create the appropriate Language object. Tralazaa!
#
# EXAMPLES
#
# lang2smd UML 1 4 UML14.xmi
# 
# AUTHORS
#
# Marcus Alanen <maalanen@abo.fi> 2003-10
#
#
#
#
import coral
SMD=coral.lang.SMD01 
import sys
from coral.modeler.NameFactory import createCoralUUID,createCoralPropertyUUID
import inspect

from coral import log
logger=log.getLogger("lang2smd")

def createSMDElement(obj, h, srclang):
    """Creates a new SMD element for obj if none exists in the hash h."""
    if h.has_key(obj):
        return h[obj]

    ret = None
    skip = 0
    if isinstance(obj, coral.Language):
        if obj.__language__() == srclang:
            return
        ret = SMD.Language.newAtom()
        h[obj] = ret
        ret.name = obj.getName()
        ret.version = obj.getVersion()
        ret.revision = obj.getRevision()
        name = str(ret.name) + str(ret.version) + str(ret.revision)
        if name == "CORE01":
            n = "coral/core/CORE01.xmi"
        elif name =="XMI12":
            n = "coral/core/XMI12.xmi"
        else:
            n = "coral/lang/" + name + "/def/" + name + ".xmi"
        st = coral.Project.getProjectByFilename(n)
        ret.__setProject__(st)
        print "*** Created new Language", ret, ret.name, ret.version, ret.revision
        ret.__setXMIid__(createCoralUUID(obj))
        return
    if isinstance(obj, coral.Package):
        ret = SMD.Package.newAtom()
        h[obj] = ret
        ret.name = obj.getName()
        ret.__setXMIid__(createCoralUUID(obj))
    if isinstance(obj, coral.PyOperation):
        ret = SMD.Operation.newAtom()
        h[obj] = ret
        ret.language = obj.getOperationLanguage()
        ret.isWFR = obj.isWFR()
        ret.name = obj.getFuncName()
        source = obj.getCode()
        for line in source.split("\n"):
            a = coral.String.newAtom()
            a.__setString__(line)
            ret.code.add(a)
    if isinstance(obj, coral.EnumerationDefinition):
        if obj.size() == 0:
            skip = 1 # Skip bad enums
            return
        ret = SMD.EnumerationDefinition.newAtom()
        h[obj] = ret
        # SHOULD DEFINITELY SET WELL-KNOWN UUID!
        ret.__setXMIid__(createCoralUUID(obj))
        ret.name = obj.getName()
        for i in xrange(obj.size()):
            ds = obj.getDescription(i)
            s = coral.String.newAtom()
            s.__setString__(ds)
            ret.values.append(s)
    elif isinstance(obj, coral.ElementDefinition):
        ret = SMD.ElementDefinition()
        h[obj] = ret
        # SHOULD DEFINITELY SET WELL-KNOWN UUID!
        ret.__setXMIid__(createCoralUUID(obj))
        ret.name = obj.getName()
        for i in obj.__properties__():
            if i.isAnonymous():
                continue
            if i.getOwner() == obj:
                createSMDElement(i, h, srclang)
                ret.properties.add(h[i])
    elif isinstance(obj, coral.DatatypeDefinition):
        ret = SMD.DatatypeDefinition()
        h[obj] = ret
        # SHOULD DEFINITELY SET WELL-KNOWN UUID!
        ret.__setXMIid__(createCoralUUID(obj))
        ret.name = obj.getName()
    elif isinstance(obj, coral.Property):
        ret = SMD.Property()
        h[obj] = ret
        ret.name = obj.getName()
        ret.isOrdered = obj.isOrdered()
        ret.isBag = obj.isBag()
        ret.isUnserializable = obj.isUnserializable()
        if obj.isAssociation():
            ret.type = 0
        elif obj.isComposition():
            ret.type = 1
        else:
            assert 0
        ret.multiplicity_lower = obj.getMultLower()
        ret.multiplicity_upper = obj.getMultUpper()
        e = obj.getTarget()
        createSMDElement(e, h, srclang)
        ret.element_kind = h[e]
        assert ret.element_kind 
        oppo = obj.getOppositeProperty()
        if oppo and oppo.isAnonymous():
            oppo = None
            
        assert ret.element_kind
        if oppo:
            assert(ret.element_kind)
            createSMDElement(oppo, h, srclang)
            assert(ret.element_kind)
            ret.opposite = h[oppo]
            assert(ret.opposite)
            if oppo.getOwner() != e:
                logger.error("ARGH")
                logger.error("owner="+unicode(obj.getOwner().getName())+"obj="+unicode(obj.getName())+"e ="+unicode(e.getName())+"  and "+unicode(oppo.getName())+", but it says "+unicode(oppo.getOwner().getName()))
                assert(0)
        assert ret.element_kind

    # NOTE! We enter this if we were
    # ElementDefinition, EnumerationDefinition, DatatypeDefinition
    if isinstance(obj, coral.AbstractElementDefinition):
        ret.isAbstract = obj.isAbstract()
        createSMDElement(obj.__package__(), h, srclang)
        for s in obj.__superclasses__():
            createSMDElement(s, h, srclang)
        for s in obj.__operations__():
            createSMDElement(s, h, srclang)

    if isinstance(obj, coral.ElementDefinition):
        ret.MOFDatatype = obj.isMOFDatatype()

    if ret:
        #print "New obj2elem " + str(obj) + " -> " + str(ret)
        return ret
    else:
        logger.error("No suitable element for " + unicode(obj) + "!")
        assert(0)

def fixSMDElement(h, obj, ret, srclang):
    """obj is the Coral object, and ret is the corresponding SMD Element.
    We should fix all the pointers correct."""
    if ret.oclIsKindOf(SMD.AbstractElementDefinition):
        # This skips AnyElement and similar stuff found
        # in other metamodels.
        # We don't need their Packages...
        if not h.has_key(obj): # or obj.__language__() != srclang:
            pass
        else:
            parent = obj.__package__()          
            if parent:
                if h.has_key(parent):
                    # this assumes that parent is in the language being translated
                    # this is not true for the elements from Core
                    ret.package = h[parent]
                else:
                    # BUG FIXME
                    logger.debug(unicode(ret)+unicode(ret.name))
                    assert(0)

            if ret.oclIsKindOf(SMD.Package):
                for i in obj.__elements__():
                    if h.has_key(i):
                        ret.elements.add(h[i])
                    else:
                        # mostly empty EnumerationDefinitions, I hope...
                        if (isinstance(i, coral.EnumerationDefinition) and i.size() == 0):
                            continue
                        # or some other language
                        if obj.__language__() != srclang:
                            continue
                        assert(0)
                return
        # superclasses & subclasses
        for i in obj.__superclasses__():
            ret.superclasses.add(h[i])
        for i in obj.__operations__():
            ret.operations.add(h[i])
        if ret.oclIsKindOf(SMD.EnumerationDefinition):
            # Already done in createSMDElement
            return
        if ret.oclIsKindOf(SMD.DatatypeDefinition):
            # ? Already done?
            return
        if ret.oclIsKindOf(SMD.ElementDefinition):
            return
    if ret.oclIsKindOf(SMD.Property):
        if not ret.element_kind:
            logger.debug("PROBLEMS"+unicode(ret)+unicode(obj))
            logger.debug("ARGH no element_kind")
            logger.debug(unicode(ret))
            logger.debug(unicode(obj)+unicode(obj.getName())+unicode(obj.getTarget().getName()))
            ret.element_kind = h[obj.getTarget()]
            assert(ret.element_kind)
            #assert(0)
        ret.__setXMIid__(createCoralPropertyUUID(obj))
        return


def lang2smd(srclang):
    
    #
    # Maps loaded elements to suitable SMD elements
    #
    obj2smd = {}

   


    language = SMD.Language()
    language.name = srclang.getName()
    language.version = srclang.getVersion()
    language.revision = srclang.getRevision()
    language.__setXMIid__(createCoralUUID(srclang))
    obj2smd[srclang] = language

    for p in srclang.__elements__():
        if not isinstance(p, coral.Package): continue
        createSMDElement(p, obj2smd, srclang)
        for e in p.__elements__():
            createSMDElement(e, obj2smd, srclang)
            #for s in e.__superclasses__():
            #    createSMDElement(s, obj2smd, srclang)


    for e in srclang.__elements__():
        createSMDElement(e, obj2smd, srclang)
        for s in e.__superclasses__():
            createSMDElement(s, obj2smd, srclang)


    language.root_ed = obj2smd[srclang.getRoot()]

    print "FIXING"
    for i in obj2smd.iterkeys():
        fixSMDElement(obj2smd, i, obj2smd[i], srclang)

    for i in obj2smd.itervalues():
        if i.oclIsKindOf(SMD.Property):
            assert(i.element_kind)
    assert(language.root_ed)

    language.URInamespace = srclang.getXMLNS()

    return language

def main():
    name, version, revision, outputfile = sys.argv[1:5]
    version = int(version)
    revision = int(revision)

    srclang = coral.Language.__getLanguage__(name, version, revision)
    assert(srclang)

    language=lang2smd(srclang)
    coral.saveModel(outputfile, language)
 

if __name__=='__main__':
    main()
