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

# CoralModeler: encapsulates some important application-wide things.
# Implements Singleton.

import coral

from qt import *
import os,sys
import traceback
import glob

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

# Using a member variable instead of a global variable seems to cause this
# crash:
# http://mats.imk.fraunhofer.de/pipermail/pykde/2003-August/005995.html
#
_qApp = None

from coral.modeler.diagram.autogen import RendererGenerator
from coral.modeler.diagram.degen import DiagramElementGenerator
from coral.modeler.diagram.xmidi_diagrams import DiagramCreator

from coral.modeler.extension import ExtensionRegistry
import coral.util.user

class CoralModeler:

    instance = None
    nwindows=0

    extensionRegistry=ExtensionRegistry()
    
    def getInstance(cls):
        if not cls.instance:
            cls.instance = cls()
        return cls.instance
    getInstance = classmethod(getInstance)


    def __init__( self ):
        self.observers = {}
        self.timer = QTimer()
        QObject.connect(self.timer,SIGNAL('timeout()'),self.processObservers)
        self.__initRegistry()
        self.debugMode = False
 
    def __initRegistry(self):    
        coral.getTransactionManager().beginModifyCmd()
        try:
            manifests=[]

            install_path = coral.installation_path
            user_home = coral.util.user.getUserHome()




            # Generate PE

            pepaths = []
            extensionpaths = self.getExtensionPaths()
            for ep in extensionpaths:
                pepaths.append(ep+"/*/*_PE.xml")
            pepaths += [install_path+"/coral/addins/*/*_PE.xml",
                        user_home + "/.coral/addins/*/*_PE.xml"]

            for pattern in pepaths:
                for f in glob.glob(pattern):
                    dirn = os.path.dirname(f)
                    # Check if we should create new PE:s
                    if not os.path.isfile(dirn + os.sep+"propertyeditors.py") or \
                           os.path.getmtime(dirn + os.sep+"propertyeditors.py")<os.path.getmtime(f):

                        index=dirn.find(".coral")
                        if index>=0:
                            moduledir=dirn[index+7:]
                        else:
                            # The property editors are in the directory coral.addins.X
                            moduledir=dirn[dirn.rfind("coral"):]
                        moduleprefix=moduledir.replace(os.sep,".")

                        # Create property editors
                        from coral.lgen.pegen.pegen import createESMDmodels
                        createESMDmodels(f, dirn, moduleprefix)


            # User prefs first...
            paths=[install_path + "/coral/modeler/dmanifest.xmi"]
            for ep in  extensionpaths:
                paths.append(ep+"/*/manifest.txt")
                paths.append(ep+"/*/*.mnf.xmi")
            paths += [
                user_home + "/.coral/coral/addins/*/manifest.txt",
                user_home + "/.coral/coral/addins/*/*.mnf.xmi",
                user_home + "/.coral/cache/*/manifest.txt",
                user_home + "/.coral/cache/*/*.mnf",
                install_path + "/coral/lang/*/editors/manifest.txt",
                install_path + "/coral/addins/*/manifest.txt",
                install_path + "/coral/lang/*/editors/*.mnf",
                install_path + "/coral/addins/*/*.mnf.xmi",
                ]

            # Fix PYTHONPATH so we can import python files from user's .coral
            # and CORALEXTENSIONPATH
            # A bit of a crude hack, but...
            sys.path.insert(0, user_home + "/.coral")
            for ep in extensionpaths:
                sys.path.insert(0,ep)

            for pattern in paths:
                manifests=manifests + glob.glob(pattern)

            for m in manifests:
                self.extensionRegistry.addManifest(m)

            RendererGenerator.updateExtensions(self.extensionRegistry)
            DiagramElementGenerator.updateExtensions(self.extensionRegistry)
            DiagramCreator.updateExtensions(self.extensionRegistry)
        finally:
            coral.getTransactionManager().endModifyCmd()
        coral.getTransactionManager().trimPast()

        for e in self.extensionRegistry.getRegisteredExtensionsByType(type="ReactiveWriterKernelComponent"):
            logger.debug("Registering ReactiveWriterKenerlComponent "+e[0])
            coral.getTransactionManager().addObserver(e[1])

    def getExtensionPaths(self):
        # for each colon-separated token in the environment variable
        # CORALEXTENSIONPATH if it exists as a path,
        # we will add it to the search paths for extensions
        coralextensionpath = os.environ.get("CORALEXTENSIONPATH",None)
        if coralextensionpath:
            extensionpaths = [path for path in coralextensionpath.split(":")
                              if os.path.exists(path)]
        else:
            extensionpaths = []
        return extensionpaths

    def getDebugMode(self):
        return self.debugMode

    def toggleDebugMode(self):
        self.debugMode = not self.debugMode
        return self.debugMode

    def setDebugMode(self, mode):
        self.debugMode = mode
   
    def getQtApp( self, args=None ):
        if not args:
            args=[]
            
        global _qApp
        if not _qApp:
            try:
                coraltracker= os.getenv("CORALTRACKER")
                if coraltracker:
                    import coral.modeler.guitracker.guitracker
                    filename = os.getenv("CORALTRACKFILE")
                    if not filename:
                        filename = "/tmp/qt-logfile"
                    if coraltracker == "RECORD":
                        track = 1
                    elif coraltracker == "PLAYBACK":
                        track = 0
                    else:
                        assert 0
                    _qApp = coral.modeler.guitracker.guitracker.GuiTracker(args, filename, track)
                else:
                    _qApp = QApplication(args)
                    # Try to mimic MS Windows if we run in cygwin
                    if sys.platform=="cygwin":                        
                        _qApp.setStyle(QWindowsStyle())
			# Set the same background color as Windows XP
			self.palette=_qApp.palette()	
			self.palette.setColor(QColorGroup.Background,QColor(236,233,216))
			self.palette.setColor(QColorGroup.Button,QColor(236,233,216))
			_qApp.setPalette(self.palette)
            except:
                logger.debug(unicode(traceback.print_exc()))
                _qApp = None
        return _qApp


    def addContinuousObserver( self, obs ):
        assert isinstance(obs,ContinuousModelObserver)
        self.observers[obs] = True
        if self.observers:
            self.timer.start(0)


    def removeContinuousObserver( self, obs ):
        assert isinstance(obs,ContinuousModelObserver)
        if obs in self.observers:
            self.observers[obs] = False


    def mainLoop( self ):
        app = self.getQtApp()
        assert app

        coral.getTransactionManager().addObserver(self.onModelChanged)

        self.timer.start(0)
        
        app.exec_loop()
        app.quit()


    def processObservers( self ):
        tobedeleted = []
        for o in self.observers:
            o.doIt()
            if not self.observers[o]:
                tobedeleted.append(o)
        for o in tobedeleted:
            del self.observers[o]

        if not self.observers:
            self.timer.stop()
            
                
    def onModelChanged( self, d ):
        for o in self.observers:
            o.onModelChanged(d)

    def quit( cls ):
        global _qApp
        if _qApp:
            _qApp.quit()

    quit=classmethod(quit)
    
    def getClipboard(self):
        global _qApp
        if _qApp:
            return _qApp.clipboard()
        return None
   
_CoralModeler=CoralModeler.getInstance()

class ContinuousModelObserver:

    """ This is an interface for objects that observe a model
    continuosly 'in the background'. doIt is called repeteadly by the
    Qt main loop when idle, and should not contain long
    computations. onModelChanged is added as a model observer via the
    TransactionManager.

    To use the ContinuousModelObserver interface subclass this interface,
    instantiate the class and call CoralModeler.addContinuousObserver and
    CoralModeler.removeContinuousObserver to activate/deactivate the
    instance.
    """

    def onModelChanged( self, d ):
        pass


    def doIt( self ):
        pass

