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

import coral
from coral import configuration
import os
import string
from coral.modeler.editor import Editor
import sys

#
#
#
config_prj=coral.Project()
config_prj.setFilename("CoralConfiguration")
config_prj.setUserProject(False)
config=coral.lang.CoralEx10.Package(name="CoralConfiguration")
config.__setProject__(config_prj)

for e in  coral.meta.CoralEx10.Addins.elements:
    coral.lang.CoralEx10.Package(name=str(e.name)+"s", owner = config)

from coral.log import getLogger
_log=getLogger("coral.extension")


def fromFullElementName2AbstractElementDefinition(name):
    langName=string.split(name,"::")
    i=string.split(langName[0],".")
    #_log.debug(unicode(i))
    l = coral.Language.__getLanguage__(i[0], int(i[1]), int(i[2]))

    e=l
    if len(langName)>1:
        for name in string.split(langName[1],"."):
            e = e.getElement(name)
    return e
 
def getModuleByName(name):
    module=None
    if name not in sys.modules:
        _log.debug("Loading module %s",name)
    try:
        module=__import__(name,globals(),locals(),name)
    except Exception:
        _log.exception("Error while loading extension module %s",name)
        raise
    return module

def getLanguages():
     entries = coral.xmlns_db_size()

     p=coral.lang.CoralEx10.Package(name="Languages", owner = config)
     
     languages = []
     for i in xrange(entries):
         # Skip SMD Packages
         if coral.xmlns_db_version(i) ==-1:
             continue
         
         e=coral.lang.CoralEx10.LanguageDescription(
             name= coral.xmlns_db_metamodelname(i),
             version = coral.xmlns_db_version(i),            
             revision= coral.xmlns_db_revision(i),
             URInamespace = coral.xmlns_db_xmlns(i),
             description = coral.xmlns_db_metamodelname(i)+" "+str(coral.xmlns_db_version(i))+"."+str(coral.xmlns_db_revision(i)),
           
             )
         p.ownedElement.append(e)
             
     return p.ownedElement


class ExtensionRegistry:
    def __init__(self):
        L=coral.lang.CoralEx10.Addins
        self.extensionsbytype={}
        self.typename2type={}
        self.edclass2attrib={
            coral.lang.CoralEx10.Addins.DiagramEditorToolbarEntry:"diagram_kind",
            coral.lang.CoralEx10.Addins.DiagramElementCreator:"element_kind",
            coral.lang.CoralEx10.Addins.DiagramElementExt:"element_kind",
            coral.lang.CoralEx10.Addins.DiagramCreator:"model_kind",
            coral.lang.CoralEx10.Addins.SimpleDiagramElementExt:"typeInfo",
            coral.lang.CoralEx10.Addins.PropertyEditor:"element_kind"
            }
   
        for e in  coral.meta.CoralEx10.Addins.elements:
            ed=e.__type__().getAbstractElementDefinitionFromSMD(e)
            self.extensionsbytype[ed]=[]
            self.typename2type[str(e.name)]=ed
            # If there is a special property such as element_kind
            # use it as the name of the extension
            if not self.edclass2attrib.has_key(ed):
                if e.properties.size()==1 and str(e.properties[0].name)[-4:]=="kind":
                    self.edclass2attrib[ed]=str(e.properties[0].name)
                else:
                    self.edclass2attrib[ed]="name"

        self.ext2factoryObject={}

    def __dict_append(self, dct, key, item):
        try:
            lst=dct[key]
        except KeyError:
            lst=[]
            dct[key]=lst
        lst.append(item)
    
    def getRegisteredExtensionsByType(self,type,profiles=[]):
        extensions_in_profile={}
        overrides=[]
        for ext in self.extensionsbytype[self.typename2type[type]]:
            if not profiles or ext.profile in profiles:
                self.__dict_append(extensions_in_profile, ext.profile, ext)
                if ext.overrides:
                    overrides.append(ext)

        # Join all profiles
        lst=[]
        for ext in extensions_in_profile.values():
            lst+=ext

        # This part takes care of the priority ordering
        for ext in overrides: #Remove the overridden extension
            toremove=ext.overrides
            try:
                lst.remove(toremove)
            except ValueError:
                pass

        
        # Sort the list according to order
        # NOT IMPLEMENTED!!!
        # And it's bs as well. There is no order...
        lst.sort(lambda x,y: x.order-y.order)

        # Convert to proper output format
        name_factory_list=[]
        for ext in lst:
            elementName=str(ext.__getSlotByName__(self.edclass2attrib[ext.__type__()])[0])
            name_factory_list.append([elementName,self.getFactory(ext)])

        return name_factory_list

    def getFactoryList(self, lst):
        return [self.getFactory(e) for e in lst]

    def getFactory(self, entry):
        L = coral.lang.CoralEx10.Addins
        if not self.ext2factoryObject.has_key(entry):
            if entry.oclIsKindOf(L.DiagramCreator) and entry.typeInfo:
                self.ext2factoryObject[entry]=unicode(entry.typeInfo)
            elif entry.oclIsKindOf(L.PresentationElement) and entry.rendererPath:
                from coral.modeler.diagram.presentation import DiagramElementExt
                pres = {}
                pres["rendererPath"]=None
                pres["layouterPath"]=None
                pres["uiPath"]=None
                for p in pres.keys():
                    extpath = unicode(getattr(entry,p))
                    if not extpath: continue
                    modulename=string.join(string.split(extpath,'.')[:-1],".")
                    className=string.split(extpath,'.')[-1]
                    module = getModuleByName(modulename)
                    if module:
                        if not module.__dict__.has_key(className):
                            raise "Extension "+str(entry.name)+": "+str(modulename)+" does not have a feature named "+str(className)
                        else:
                            pres[p] = module.__dict__[className]
                # Pass all parameters even if some are 'None'
                self.ext2factoryObject[entry] = DiagramElementExt(pres["rendererPath"],
                                                                  layouter=pres["layouterPath"],
                                                                  userInterface=pres["uiPath"])          
                #_log.debug("Presentation Element "+ unicode(entry.path) + " replaced with model based information")
            else:
                if entry.oclIsKindOf(L.PresentationElement):
                    _log.debug("Extension "+unicode(entry.name)+": "+unicode(entry.path)+" uses old, textual format for presentation information")
                modulename=string.join(string.split(str(entry.path),'.')[:-1],".")
                className=string.split(str(entry.path),'.')[-1]
                module = getModuleByName(modulename)
                if module:
                    if not module.__dict__.has_key(className):
                        raise "Extension "+str(entry.name)+": "+str(modulename)+" does not have a feature named "+str(className)
                    else:
                        self.ext2factoryObject[entry]=module.__dict__[className]            

        return self.ext2factoryObject[entry]

    def getToolByName(self,name):
        for t in config.ownedElement.select(lambda c: c.name=="ExtensionTools")[0].ownedElement:
            if str(t.name)==name:
                return self.getFactory(t)
            
    def addManifest(self, fileName):
        if not fileName[-4:]==".xmi":
            return self.addOtherManifest(fileName)

        manifest=coral.loadInternalDocument(fileName)
        profile=manifest.getFilename().split("/")[-2]
        
        for m in manifest.contents():
            for entry in m.__getAllParts__():
                if entry.oclIsKindOf(coral.lang.CoralEx10.Profile):
                    self.registerProfile(entry)
                    continue
                if not entry.oclIsKindOf(coral.lang.CoralEx10.Addins.ExtEntry):
                    continue
                entry.profile=profile
                self.registerEntry(entry)

        manifest.invalidate()

    def registerProfile(self, profile):
        assert profile.oclIsKindOf(coral.lang.CoralEx10.Profile)

        if not config.ownedElement.exists(lambda e: e.name == "Profiles"):
            profiles=coral.lang.CoralEx10.Package(name="Profiles",owner=config)

        profiles=config.ownedElement["Profiles"]
        profiles.ownedElement.append(profile)
        
    def registerEntry(self, entry):
        assert entry.oclIsKindOf(coral.lang.CoralEx10.Addins.ExtEntry)
        if  self.extensionsbytype.has_key(entry.__type__()):
            self.extensionsbytype[entry.__type__()].append(entry)
        config.ownedElement[str(entry.__type__().getName())+"s"].ownedElement.append(entry)
    def register(self,type,elementName,factoryName,priority,order):
        assert(len(string.split(str(factoryName),'.'))>=2)

        ExtClass=coral.lang.CoralEx10.Addins.getElement(type)
        if not ExtClass:
            print "Unknown extension type",type
            _log.error("Unknown extension type %s",type)
            return
        
        newExtension=ExtClass()
        newExtension.__getSlotByName__(self.edclass2attrib[newExtension.__type__()])[0].__setString__(elementName)
        # newExtension.__dict__[self.edclass2attrib[newExtension]]=elementName
        newExtension.path=factoryName

        self.extensionsbytype[ExtClass].append(newExtension)
        

    def addOtherManifest(self, fileName):
        line=1
        fd=open(fileName,"r")
        try:
            for l in fd.readlines():
                w = string.split(l,"#")[0]  # strip comments
                w = string.split(w)         # split into tokens
                if w:
                    w=string.split(l)
                    if len(w)!=3:
                        _log.error("Invalid syntax in extension manifiest file %s, line %d",fileName, line)
                    else:
                        try:
                            self.register(w[0],w[1],w[2],0,0)
                            _log.debug("Extension registered %s\n      %s %s",w[0],w[1],w[2])
                        except:
                            _log.exception("Exception while registering extension. Manifiest file %s, line %d",fileName,line)
                            sys.exit(1)
                line=line+1
        finally:
            fd.close()






class ContextMenuAction:

    def createMenuItem(self, *args):
        pass
   

class ModelerTool:
    def __init__(self):
         pass

class PropertyEditor(Editor):
       
    def __init__(self,parent,manager):
        Editor.__init__(self)
        self.manager=manager
        self.edited=0
        
    def supercedes(self):
        return []

    def canEdit(self,e):
        return 0

    def importance(self):
        return 0

    def setEdited(self,edit=1):
        self.edited=edit
        self.manager.setEdited(edit)
        
    def changed(self,text=""):
        self.setEdited(1)



class DiagramEditorToolbarEntry:

    def getLabel( cls ):
        return "*Define getLabel to change this text*"
    getLabel = classmethod(getLabel)
    
    def getIcon( cls ):
        return None
    getIcon = classmethod(getIcon)

    def doIt( self, diagram, parent, hints=[] ):
        assert 0



def getFactory(path):
    path=str(path)
    factoryObject=None
    modulename=string.join(string.split(path,'.')[:-1],".")
    className=string.split(path,'.')[-1]
    module=getModuleByName(modulename)
    if module:
        if not module.__dict__.has_key(className):
            raise "Extension "+modulename+" does not have a feature named "+className
        else:
            factoryObject=module.__dict__[className]
    return factoryObject
	
