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

from coral.modeler.window import CoralMainWindow,CoralTabWidget
from coral import modeler
from coral.modeler import iconcache

import sys
import glob
import os.path
import qt
import traceback
import string

import coral
from coral.modeler.coralmodeler import CoralModeler
from coral.modeler import editor

from coral.modeler import extension,action

import coral.modeler.project

from coral.log import getLogger
from coral import configuration

DI = coral.lang.XMIDI20

_log=getLogger("coral.modeler.modeler")


theClipboard=[]

n = 0
def perpetualEnumerator():
    global n
    n += 1
    return n

def chooseLanguage():
    from coral.modeler.dlg.LanguageSelectionDlgEx import LanguageSelectionDlgEx
    gl = extension.getLanguages()
    lindex=LanguageSelectionDlgEx().main(gl)
    if lindex==-1:
        return None
    return gl[lindex]

def chooseTemplate():
    install_path = coral.installation_path
    user_home = coral.util.user.getUserHome()

    from coral.modeler.dlg.TemplateSelectionDlgEx import TemplateSelectionDlgEx
    
    templatePaths = []
    files2=[]
    templateFiles = {}
    
    templatePaths += [install_path+"/coral/addins/*/mtemplates/*.xml",
                  user_home + "/.coral/addins/*/mtemplates/*.xml"]

    for pattern in templatePaths:
        for f in glob.glob(pattern):
            dirn = os.path.dirname(f)
            files=os.listdir(dirn)
            for foo in files:
                if str(foo)[-4:]==".xml":
                    files2.append(foo)
                    
            for fil in files2:
                templateFiles[fil] = dirn+"/"+fil
            files2=[]
            
    tempKey = str(TemplateSelectionDlgEx().main(templateFiles))+".xml"
    if not templateFiles.has_key(tempKey):
        return None
    return templateFiles[tempKey]
                
    
        


class ModelerWindow(CoralMainWindow,editor.Editor):

    def __showAboutWindow(self, opid):
        from coral.modeler.dlg import AboutDlg
        a = AboutDlg.about(self)
        a.version.setText(str(coral.version))
        a.build.setText(str(coral.build_date))
        #a.build.hide() # Currently unused
        #a.buildLabel.hide()
        a.show()
        
        
    def __init__(self, show = True):

        CoralModeler.nwindows+=1
        CoralMainWindow.__init__(self)
        editor.Editor.__init__(self)

        qp = qt.QPopupMenu(self)
        qp.insertItem("About Coral...", self.__showAboutWindow)
        mb = self.menuBar()
        mb.insertSeparator()
        mb.insertItem("Help", qp)
        

        # This is the default geometry. Proper widget geometry will be
        # loaded from configuration when the modelerwindow is shown.
        self.resize(600,400)

        self.extensionTools=[]
        self.split = None
        self.split2 = None
        self.split3 = None
        
        coral.getTransactionManager().addBusyObserver(self.__kernelBusy)
        coral.getTransactionManager().addObserver(self.onModelChanged)

        self.editorSet=editor.EditorSet()
        
        coral.getTransactionManager().beginModifyCmd()
        try:
            self.__initExtensions()

            self.toolBar.setLabel("Coral")
            for a in ["saveProject","undo","redo"]:
                action[a].addTo(self.toolBar)

            for a in ["newProject","newProjectTemplate","coralPathEdit","openProject","cloneWindow","quit"]:
                action[a].setEnabled(1)

            self.createEditors()

        finally:
            coral.getTransactionManager().endModifyCmd()
        coral.getTransactionManager().trimPast()
        
        self.setIcon(iconcache.getIcon("coral"))
        self.main(loop=0)
        if show:
            self.show()
   
    def initActions(self):      
        return [

            modeler.CoralAction(self,"quit","modeler.quit", "&Quit",
                        qt.QIconSet(iconcache.getIcon("exit")),
                        qt.Qt.CTRL+qt.Qt.Key_Q),

            self.newProject,
            self.newModel,
            modeler.CoralAction(self,"newProjectTemplate","modeler.newProjectTemplate","New Project from &Template...",
                    qt.QIconSet(iconcache.getIcon("filenewm")),
                    qt.Qt.CTRL+qt.Qt.Key_T),
            
            modeler.CoralAction(self,"coralPathEdit","modeler.coralPathEdit","&Edit coralpath...",
                    qt.QIconSet(iconcache.getIcon("misc")),
                    qt.Qt.CTRL+qt.Qt.Key_E),
            
            modeler.CoralAction(self,"openProject","modeler.openProject","&Open Project...",
                    qt.QIconSet(iconcache.getIcon("fileopen")),
                    qt.Qt.CTRL+qt.Qt.Key_O),
            modeler.CoralAction(self,"saveProject","modeler.saveProject","&Save Project",
                    qt.QIconSet(iconcache.getIcon("filesave")),
                    qt.Qt.CTRL+qt.Qt.Key_S),
            modeler.CoralAction(self,"saveProjectAs","modeler.saveProjectAs","Save Project &As..",
                    qt.QIconSet(iconcache.getIcon("filesaveas")),
                    0),
            modeler.CoralAction(self,"cloneWindow","modeler.clone","&New Window",
                        qt.QIconSet(iconcache.getIcon("clone")),
                        qt.Qt.ALT+qt.Qt.Key_N),

            undo,
            redo,
                     
            
            ]

    def initMenu(self):
        return [
            ( "File",( "newProject","newProjectTemplate","coralPathEdit","newModel","openProject","saveProject",
                      "saveProjectAs","quit" )  ),
            ("Edit",["undo","redo"] ),
            ( "Tool", []),
            ( "Window",["cloneWindow"])
            ]
            
        
    def newProject(self,language=None):
        """
        CoralAction: newProject
        Group: modeler
        Label: &New Project...
        Icon: filenewm
        Accelerator: CTRL+1
        
        Create a new project
        """

  
        # Create new Project and new top-level element for it.
        # The project has no name, though

        project=None
        if not language:
            language = chooseLanguage()
 
            
        if not language:
            return
        

        coral.getTransactionManager().beginModifyCmd()
        try:
            project=coral.Project()
            project.setFilename("Project%d.xml"%perpetualEnumerator())
            self.newModel(project,language)
        finally:
            coral.getTransactionManager().endModifyCmd()
            
        if not project:
            return None

        return project
        
        
    def newModel(self,project=None,language=None):
        """
        CoralAction: newModel
        Script: modeler.newModel
        Label: &New Model...
        Icon: filenew
        Accelerator: CTRL+N
        
        Create a new model
        """
        ## If no project is passed, try to retrieve it from the focus
        if not project:
            project=self.focus.__getRootsProject__()
            ## If no valid element has focus, don't continue
            if not project:
                _log.debug("Cannot insert new model into project that does not exist")
                return
        
        if not language:
            # chose a language        
            language = chooseLanguage()
        else:
            if type(language)==type([]) or type(language)==type((1,2)):
                language=coral.lang.CoralEx10.LanguageDescription(name=language[0],version=language[1],revision=language[2])
        if not language:
            return None

        coral.getTransactionManager().beginModifyCmd()
        try:
            kl=coral.Language.__getLanguage__(str(language.name),language.version,language.revision)
            assert(kl)
            new=kl.getRoot()()
            new.__setProject__(project)
            if self.editorSet:
                self.editorSet.notifyFocusOn(new)
        finally:
            coral.getTransactionManager().endModifyCmd()

    def coralPathEdit(self):
        from coral.modeler.dlg.CoralPathDlg import CoralPathDlg
        c=CoralPathDlg().main()
        if not c == -1:
            coral.setCoralPath(c)

    def newProjectTemplate(self):
        a = chooseTemplate()
        if a:
            coral.getTransactionManager().beginModifyCmd()
            try:
                tempPrj = coral.loadDocument(a)
                set = coral.Set()
                for e in tempPrj.contents():
                    set.append(e)
                newSet = coral.ModelCopy(set)                
                prj = coral.Project()
                for e in newSet:
                    e.__setProject__(prj)
                tempPrj.invalidate()
            finally:
                coral.getTransactionManager().endModifyCmd()

    def openProject(self,fn=""):
        return coral.modeler.project.openProject(fn)
    
    def saveProject(self):
        if self.focus:
            project = self.focus.__getRootsProject__()
            coral.modeler.project.saveProject(project=project)

    
    def saveProjectAs(self,fileName="",xmiVersion=""):
        if self.focus:
            project = self.focus.__getRootsProject__()
            coral.modeler.project.saveProjectAs(fileName,xmiVersion,project=project)

    def clone(self):
        return ModelerWindow()
        
    def close(self,x=0):    
        CoralModeler.nwindows-=1

        if not CoralModeler.nwindows:
            _log.debug("Last window")
            self.__saveMWState()
            CoralModeler.quit()

        return qt.QMainWindow.close(self,x)


    def __saveMWState( self ):
        def storeWidgetStateViaOperatorLtLt( w, ns ):
            s = qt.QString("")
            ts = qt.QTextOStream(s)
            ts << w
            self.config.addOption(ns,unicode(s),False)

        def saveSplitterState( w, ns ):
            self.config.addOption(ns,repr(w.sizes()),False)

        storeWidgetStateViaOperatorLtLt(self,["GUI","dockstate"])
        if self.split:
            saveSplitterState(self.split,["GUI","split"])
        if self.split2:
            saveSplitterState(self.split2,["GUI","split2"])
        if self.split3:
            saveSplitterState(self.split3,["GUI","split3"])

        self.config.addOption(["GUI","geometry"],repr(self.geometry().rect()),
                              False)

        self.config.saveConfig()
        

    def __loadMWState( self ):
        def loadWidgetStateViaOperatorGtGt( w, ns ):
            state = self.config.dGet(ns)
            if state:
                s = qt.QString(state)
                qt.QTextIStream(s) >> w

        def loadSplitterState( w, ns ):
            splitsizes = self.config.dGet(ns)
            if splitsizes:
                w.setSizes(eval(splitsizes))

        if self.config.dGet(["GUI","dont_restore_geometry"]):
            return
        
        geom = self.config.dGet(["GUI","geometry"])
        if geom:
            apply(self.setGeometry,eval(geom))

        loadWidgetStateViaOperatorGtGt(self,["GUI","dockstate"])

        if self.split:
            loadSplitterState(self.split,["GUI","split"])
        if self.split2:
            loadSplitterState(self.split2,["GUI","split2"])
        if self.split3:
            loadSplitterState(self.split3,["GUI","split3"])

    def displayDiagram(self, diagram):
        """Makes the modelerwindow focus on the given Diagram
        by notifying all the editors.
        Returns the DiagramView of the diagram.
        Pumps QT, so the change is visible after returning from
        this function.
        """
        assert diagram.oclIsKindOf(DI.Diagram)
        self.getDiagramEditorManager().editorSet.notifyFocusOn(diagram)
        self.processEvents()
        dm = self.getDiagramEditorManager()
        return dm.getEditorForDiagram(diagram)

    def focusOn(self, e):
        """Internal focusOn function used in notification.
        Do not use."""
        self.focus=e
            
        if self.focus:
            action["newModel"].setEnabled(1)
            action["saveProject"].setEnabled(1)
            action["saveProjectAs"].setEnabled(1)
            action["cloneWindow"].setEnabled(1)
        else:
            action["newModel"].setEnabled(0)
            action["saveProject"].setEnabled(0)
            action["saveProjectAs"].setEnabled(0)
            action["cloneWindow"].setEnabled(0)

    #def getTab(self,x,y):
    #    return self.tabs[(x,y)]
    
    def addToolToTab(self,tab,newTool):
        if isinstance(newTool,qt.QWidget):
            text=newTool.caption()
            if not text:
                text="tool"
            if newTool.icon():
                self.tabs[tab].addTab(newTool,qt.QIconSet(newTool.icon()),text)
            else:
                self.tabs[tab].addTab(newTool,text)
            self.tabs[tab].showPage(newTool)
            newTool.show()
        return newTool
    
    def createEditors(self):
        _log.info("createEditors")
     
        self.tabs={}
        self.ewins=[]
       
        Layout = qt.QGridLayout(self.workspace,1,1,1,1,"Form1Layout")

        self.split=qt.QSplitter(self.workspace)
        Layout.addWidget(self.split,0,0)
        
        self.split3=qt.QSplitter(qt.Qt.Vertical,self.split)

        self.split2=qt.QSplitter(qt.Qt.Vertical,self.split)

        self.tabs[(0,0)] = CoralTabWidget(self.split3)
        self.tabs[(1,0)] = CoralTabWidget(self.split2)
        self.tabs[(0,1)] = CoralTabWidget(self.split3)
        self.tabs[(1,1)]= CoralTabWidget(self.split2)
        

        for t in extension.config.ownedElement.select(lambda c: c.name=="ExtensionTools")[0].ownedElement:
            if str(t.startInTabs):
                x=1
                y=1
                if "," in str(t.startInTabs):
                    x=int(string.split(str(t.startInTabs),",")[0])
                    y=int(string.split(str(t.startInTabs),",")[1])
                editor=CoralModeler.extensionRegistry.getFactory(t)(self.tabs[(x,y)])
                self.addToolToTab((x,y),editor)
                self.ewins.append(editor)
                                    
        for e in [self]+self.ewins:
            self.editorSet.link(e)


	self.__loadMWState()
        self.split.show()

    def createEditor(self,name):
        editor=CoralModeler.extensionRegistry.getToolByName(name)(self.tabs[(1,1)])
        if editor:
            self.addToolToTab((1,1),editor)
            self.editorSet.link(editor)

        #print editor, "hit"
            
        return editor
    
    def __kernelBusy(self,isBusy):
        if isBusy:
            qt.QApplication.setOverrideCursor(qt.QCursor(qt.Qt.WaitCursor))
        else:
            qt.QApplication.restoreOverrideCursor()
            
    def onModelChanged(self,direc):
        _log.debug("Begin ModelerWindow.onModelChanged")

        tm=coral.getTransactionManager()

        if tm.canUndo():
            action["undo"].setEnabled(1)
        else:
            action["undo"].setEnabled(0)
        if tm.canRedo():
            action["redo"].setEnabled(1)
        else:
            action["redo"].setEnabled(0)

        cf = self.focus
        if cf and cf.__getRootsProject__():
            action["newModel"].setEnabled(1)
            action["saveProject"].setEnabled(1)
            action["saveProjectAs"].setEnabled(1)
        else:
            action["newModel"].setEnabled(0)
            action["saveProject"].setEnabled(0)
            action["saveProjectAs"].setEnabled(0)
            
        _log.debug("End ModelerWindow.onModelChanged")
          
    def getCurrentFocus(self):
        return self.focus
    
    def __initExtensions(self):

        toolMenu=None
        for x in self.privateMenuBar:
            if x[0]=="&Tool":
                toolMenu=x[1]

        l = []
        for e in CoralModeler.extensionRegistry.getRegisteredExtensionsByType(type="ExtensionTool"):
            if e[1] in self.extensionTools:
                continue
            l.append(e)

        l.sort(lambda a, b: cmp(str(a[0]), str(b[0])))

        for e in l:
            self.extensionTools.append(e[1])
            tool=ToolLaunch(e[1],self.editorSet)
            name=str(e[0])
            label=name
            name=string.replace(name," ","")
            action.__declareAction__(
                modeler.CoralAction(self,"open"+name,tool.doIt,label,
                                    qt.QIconSet(iconcache.getIcon("tool")),
                                    0))
            
            action["open"+name].addTo(toolMenu)                
            action["open"+name].setEnabled(1)

    # This mehtods should dissapear
    def getOutlineEditor(self):
        return self.createEditor("Model Outline")

    def getShell(self):
        return self.createEditor("Shell")
        
    def getPropertyEditorManager(self):
        return self.createEditor("Property Editor")
    
    def getDiagramEditorManager(self):
        return self.createEditor("Diagram Editor")


class ToolLaunch:
    def __init__(self,tool,editorSet):
        self.tool=tool
        self.editorSet=editorSet
    def doIt(self,tab=(1,1)):
        window=action.getCurrentHandler("modeler")
        instance=self.tool(window.tabs[tab])
        window.addToolToTab(tab,instance)
        if self.editorSet and isinstance(instance,editor.Editor):
            ##print "link ",instance
            self.editorSet.link(instance)
   

def undo():
    """
    CoralAction: undo
    Label: &Undo
    Icon: undo
    Accelerator: CTRL+Z
    """
    if coral.getTransactionManager().canUndo():
        coral.getTransactionManager().undo()
        
def redo():
    """
    CoralAction: redo
    Label: &Redo
    Icon: redo
    Accelerator: CTRL+SHIFT+Z
    """
    if coral.getTransactionManager().canRedo():
        coral.getTransactionManager().redo()


def main(filenames):
    mw=ModelerWindow()

    for xmifile in filenames:
        try:
            p = action.openProject(xmifile)
            if not p:
                continue
            # BUG update the outliner
        except Exception,e:
            print "Error loading",xmifile+":",e
            sys.exit(1)
        
    CoralModeler.getInstance().mainLoop()

    return mw
