
#!/usr/bin/env python

__layer__ = 'CodeGen'

import coral.lang.UML14 as metamodel
import unittest
import string
from smw.transform.uml2py.uml2py import *
import os.path
import os
import sys
import popen2
from smw.transform.modeltransformer import *
import tempfile
import time


class SfiManager:
    
    def setUp(self):

        self._layerManager=LayerManager()
        self.__codePaths={}
        #self.__returnCode="--**##\"\"#.-"
        self.__tmpPath=None
        self.__pythonIn='python2'
        self.usedTmpPath=None
        self.codeGenTimestamp={}
        self.applicationTimestamp=0
        self.methods=0
        self.bidirect=0
        self.pyth=1
        self.methodPadding=1
        self.classPadding=1
        self.path=''
        self.verifications=1
        
    #def returnCode(self):
    #    return self.__returnCode

    def generateCode(self, model, path = None, bidirect=None,
                     pyth=None, methodPadding=None,
                     classPadding=None, verifications=None):
        """
        Turns the currentProject UML model into .py files.
        If path is not given, we will use the current working directory
        If operation was succesful, the project object is returned, else None.
        This operation also changes the timestamp of this file.
        """


        if path is None:
            path=self.path
        if bidirect is None:
            bidirect=self.bidirect
        if pyth is None:
            pyth=self.pyth
        if methodPadding is None:
            methodPadding=self.methodPadding
        if classPadding is None:
            classPadding=self.classPadding
        if verifications is None:
            verifications=self.verifications
      
        
        if path and ((os.path.isdir(path)) and not os.access(path,os.W_OK)):
            print os.path.isdir(path)
            ret="""
            Current path in options is not valid: you don't have the
            rights to modify the specified directory.
            
            """
            print ret
            return ret

        if path and os.path.isfile(path):
            ret="""
            Current path in options is not valid: the specified path name
            is already used by a normal file.
            """
            print ret
            return ret

        modifiedElements = []
        modifiedPackages = {}
        
        if self.codeGenTimestamp[self.currentProject().name]:
            ts_flg = 0
            for e in eles:
                # check if there is later modified elements
                # that has newer timestamp than codegentimestamp is
                if self.codeGenTimestamp[self.currentProject().name] < e.__timestamp__:
                    ts_flg = 1
                    modifiedElements.append(e)
                    # add modified packages to the list
                    if e.oclIsKindOf(metamodel.Classifier):
                        modifiedPackages[e.namespace]=''
                    if e.oclIsKindOf(metamodel.Feature):
                        modifiedPackages[e.owner.namespace]=''
                    if e.oclIsKindOf(metamodel.Package):
                        modifiedPackages[e]=''
        

            if not ts_flg:
                ret = """
                No need for code generation:
                Current project has not been modified after
                last code generation.
                """
                print ret
                return ret
        elif not self.codeGenTimestamp[self.currentProject().name]:
            modifiedPackages[self.currentProject()]=''
        # remove such packages that are inside other packages that are generated
        
        for pack in modifiedPackages.keys():
            ns=pack
            while not ns.oclIsKindOf(metamodel.Model):
                ns=ns.namespace
                if ns in modifiedPackages.keys():
                    if modifiedPackages.has_key(pack):
                        del modifiedPackages[pack]
                        break
                modifiedPackages[pack]=ns.name+'/'+modifiedPackages[pack]
        

        if pyth:
            self.__pythonIn='python2'
        else:
            self.__pythonIn='python1.5'
            
        if not path:
            if not self.__tmpPath:
                self.__tmpPath=tempfile.mktemp()
                self.usedTmpPath=self.__tmpPath
            #else:
                #popen2.popen2("rm -r "+self.__tmpPath)
                #FIXME
                #pass
            
        else:
            self.__tmpPath=path
        sys.path.append(self.__tmpPath)

        u2p=UML2Py()
        u2p.bidirectAssoc=bidirect
        u2p.verifications=verifications
        u2p.methodPadding=methodPadding
        u2p.classPadding=classPadding
        u2p.model=self.currentProject()
        u2p.metamodel=MetaMM.getMetamodelByName(u2p.model.__module__)
        if bidirect:
            c=PythonCodeArtifact(self.__tmpPath+'/'+self.currentProject().name+'/'+UML2Py._UMLClassModule)
            c.addText(UML2Py._umlcoresupport(u2p))
            c.save()

        for pack in modifiedPackages:
            sys.__stdout__.write(pack.name)
            code=UML2Py.processPackage(u2p,pack,self.__tmpPath+'/'+modifiedPackages[pack])
            if code:
                code.save()
        
        self.__codePaths[self.currentProject()]=self.__tmpPath+"/"
        self.codeGenTimestamp[self.currentProject().name] = time.time()

        return self.currentProject() # return the project if operation was succesfull
        
    def isCodeGenerated(self):
        eles=self._layerManager.returnElements(self.currentProject())
        for e in eles:
            if self.codeGenTimestamp[self.currentProject().name] < e.__timestamp__:
                return 0
        return 1
        

    def renameElement(self,element,newName):
        self.history().beginStrictTransaction()
        changedName=UndoRedoSfiManager.renameElement(self,element,newName)
        app=element.taggedValue.select(lambda x:x.name=="application")
        if changedName and len(app)==1:
            self.setApplication(element)
        self.history().endTransaction()
        


    def setApplication(self,applicationClass,parameters=""):
        if(not self.currentProject()):
            return
        elements=self._layerManager.returnElements(self.currentProject())
        if (applicationClass in elements):
            for x in elements:
                if(x.oclIsKindOf(metamodel.Class)):                    
                    for y in x.taggedValue.select(lambda z: z.name=="application"):
                        self._layerManager.detachTag(y)
                              
            self._layerManager.attachTag(applicationClass,"application",parameters)
            
            return applicationClass

                        
    def execute(self,qprocess=None):

        #chekkaa onko koodi generoitu....
        
        if(self.__codePaths.has_key(self.currentProject())):

            # Avataan samassa hakemistossa kuin minne itse koodi generoidaan
            path=self.__codePaths[self.currentProject()]
            elements=self._layerManager.returnElements(self.currentProject())
            applicationTagValue=None
            applicationTag=None
            cl=None
        
            for x in elements:
                if x.oclIsKindOf(metamodel.Class):
                    for y in x.taggedValue.select(lambda z: z.name=="application"):
                        applicationTagValue=y.dataValue
                        applicationTag=y
                        cl=x
                        
            if not applicationTag or not cl:
                return               
                
            if applicationTag.__timestamp__>self.applicationTimestamp:
                # pitäisi tietää minne directoryyn pistetään
                runFile=open(self.__tmpPath+"/runme.py","w")

                runFile.write("#!/usr/bin/env "+self.__pythonIn+"\n")

                ns=cl.namespace
                p=ns.name
                while not ns==self.currentProject():
                    ns=ns.namespace
                    p=ns.name+'.'+p

                classes=cl.namespace.ownedElement.select(lambda l:l.oclIsKindOf(metamodel.Class))
                for x in classes:
                    runFile.write("from "+p+" import "+x.name+"\n")
                runFile.write("# end of automatically created imports\n")

                runFile.write(applicationTagValue)

                runFile.close()
                self.applicationTimestamp=applicationTag.__timestamp__
 
            # runs the program
            if qprocess:
                qprocess.addArgument(self.__pythonIn)
                qprocess.addArgument(self.__tmpPath+"/runme.py")
                qprocess.start()
                return
            else:
                # [out,y,err]=popen2.popen3(os.spawnlp(os.P_NOWAIT,"python","",self.__tmpPath+"/runme.py"))
            
                [out,y,err]=popen2.popen3(self.__pythonIn+' '+self.__tmpPath+"/runme.py")

                errors=0
                res=""
                while 1:
                    t=err.readline()
                    if (t==""):
                        break
                    res=res+t
                    errors=1
                if not errors:
                    while 1:
                        s=out.readline()
                        if (s==""):
                            break
                        res=res+s
                print res
                return res


    def runTest(self,testCaseClass,qprocess=None):
        
        print "calling runTest"
               
        if not self.__codePaths.has_key(self.currentProject()):
            return
        if not testCaseClass or testCaseClass==[]:
            return

        ok=1
        for x in testCaseClass:
            if not(x in self._layerManager.returnElements(self.currentProject())):
                ok=0

        if ok:
        
            path=self.__codePaths[self.currentProject()]
            runFile=open(self.__tmpPath+"/runTest.py","w")
            runFile.write("#!/usr/lib/env "+self.__pythonIn+"\n")
            runFile.write("import unittest\n")

            for x in testCaseClass:

                importFlag=0
                inheritFlag=0
                ownerElement=x.namespace
                for y in ownerElement.clientDependency.select(lambda z: z.supplier[0].name=="unittest"):
                    importFlag=1
                    break

                if importFlag:
                    packChain=""
                    iterator=x

                while 1:
                    if(iterator.namespace is None):
                        if packChain[-1]==".":
                            packChain=packChain[:-1]
                        break
                        
                    else:
                        packChain=iterator.namespace.name+"."+packChain
                        iterator=iterator.namespace

                runFile.write("from "+packChain+" import "+x.name+"\n\n") 

            runFile.write("unittest.main()\n")
            runFile.close()

            if qprocess:
                qprocess.addArgument(self.__pythonIn)
                qprocess.addArgument(self.__tmpPath+"/runTest.py")
                qprocess.start()
                return
            
            else:
                if 1:
                    [o1,y1,result]=popen2.popen3(self.__pythonIn+' '+self.__tmpPath+"/runTest.py")
                    errors=0
                    ticker=0
                    counter=0
                    res=""
                    
                    while 1:
                        t = o1.readline()
                        if (t==""):
                            break
                        res=res+t

                    while 1:
                    
                        t=result.readline()
                        if (t==""):
                            break
                        if ticker:
                            counter+=1

                        if (string.find(t,"ImportError")>=0):
                            errors=1

                        if (string.find(t,"SyntaxError")>=0):
                            errors=1

                    
                        if (string.find(t,"Ran ")==0 and string.find(t," tests in ")>0):
                            ticker=1
                        
                        if counter==2:
                            if string.find(t,"FAILED")==0:
                                errors=1
                        res=res+t

                    # popen2.popen2("rm runTest.py")
                    text=""

                    # update the correctness tag in class
                    if errors==0:
                        text="test passed"
                        print text
                        for x in testCaseClass:
                            cl=self.layerManager().getTestSupplier(x)
                            self.updateModelTags(cl,0)
                    
                    else:
                        text="test failed"
                        print text
                        for x in testCaseClass:
                            cl=self.layerManager().getTestSupplier(x)
                            self.updateModelTags(cl,1)
                
                    for x in testCaseClass:
                        clas=self._layerManager.getTestSupplier(x)
                    
                        if clas:
                            for x in clas.taggedValue.select(lambda z: z.name=="correctness"):
                                x.dataValue=text

                    print res 
                    return res


    def checkForErrors(self,message,element):

        if not element:
            return

        if not isinstance(element,metamodel.ModelElement):
            return

        mes=string.split(message,"\n")
        errors=0
        ticker=0
        counter=0

        for z in mes:
                
            t=z

            if ticker:
                counter+=1

            if (string.find(t,"NameError")>=0):
                errors=1

            if (string.find(t,"FAILED (")>0):
                errors=1
                
            if (string.find(t,"ImportError")>=0):
                errors=1

            if (string.find(t,"SyntaxError")>=0):
                errors=1

            if (string.find(t,"ERROR")>=0):
                errors=1
                
            if (string.find(t,"Ran ")==0 and string.find(t," tests in ")>0):
                ticker=1
                        
            if counter==2:
                if string.find(t,"FAILED")==0:
                    errors=1

        text=""
        pText=""

        if errors==0:
            text="test passed"
            pText="all tests passed"
            self.updateModelTags(element,0)
        else:
             text="test failed"
             pText="some tests failed"
             self.updateModelTags(element,1)

        if element.oclIsKindOf(metamodel.Class):
            for x in element.taggedValue.select(lambda z: z.name=="correctness"):
                x.dataValue=text 
            return
        else:
            for x in element.taggedValue.select(lambda z: z.name=="correctness" ):
                x.dataValue=pText
                    
            eles=self.layerManager().recursiveElements(element)
            for x in eles:
                for y in x.taggedValue.select(lambda z: z.name=="correctness" ):
                    if element.oclIsKindOf(metamodel.Class):
                        y.dataValue=text
                    elif element.oclIsKindOf(metamodel.Package):
                        y.dataValue=pText

    def runAllTestsInPackage(self,p,qprocess=None):

        elemList=self._layerManager.recursiveElements(p)
        result=""
        els=[]
        for el in elemList:
            if el.oclIsKindOf(metamodel.Class) and not self.layerManager().isTestClass(el):
                
                els.append(self.layerManager().getTestClient(el))

        if els:
            some=self.runTest(els,qprocess)
            result=some
            
        return result
      

    def runAllTests(self,qprocess=None):
        
        elementList=self._layerManager.returnElements(self.currentProject())
        result=''

        els=[]
        for el in elementList:
            for tag in el.taggedValue.select(lambda z: z.name=="testcase"):
                els.append(el)

        if els:
            some=self.runTest(els,qprocess)
            result=some
                                       
        return result
    

    def updateModelTags(self,Class,failed):
        # eli jos failasi
        if not Class:
            return
        ele=Class
        if failed:
            running=1
            while ele.namespace and running:
                if ele.namespace.oclIsKindOf(metamodel.Model):
                    for x in ele.namespace.taggedValue.select(lambda z: z.name=="correctness"):
                        x.dataValue="some tests failed"
                    running=0
                else:
                    for x in ele.namespace.taggedValue.select(lambda z: z.name=="correctness" ):
                        x.dataValue="some tests failed"
                    ele=ele.namespace
        else:
            undef=0
            correct=1
            while ele.namespace and correct:
                ele=ele.namespace
                for x in ele.ownedElement:
                    if x!=Class:
                        for y in x.taggedValue.select(lambda z: z.name=="correctness"):
                            if y.dataValue=="some tests failed" or y.dataValue=="test failed":
                                correct=0
                            if y.dataValue=="undefined":
                                undef=1
                if correct:
                    if not undef:
                        for x in ele.taggedValue.select(lambda z: z.name=="correctness"):
                            x.dataValue="all tests passed"
                    else:
                        
                        for x in ele.taggedValue.select(lambda z: z.name=="correctness" ):
                            x.dataValue="undefined"

    def newProject(self, projectName = None, test=0):
        p=UndoRedoSfiManager.newProject(self,projectName,test)
        if p:
            self.codeGenTimestamp[p.name]=0
            return p
        
    def loadProject(self, fname):
        p=UndoRedoSfiManager.loadProject(self, fname)
        if p:
            self.codeGenTimestamp[p.name]=0
            return p

    def renameProject(self, oldName, newName):
        p=UndoRedoSfiManager.renameProject(self,oldName, newName)
        if p:
            self.codeGenTimestamp[p]=self.codeGenTimestamp[oldName]
            del self.codeGenTimestamp[oldName]
            return p
        
    def updateCorrectnessTag(self):

        fail=0
        undef=0
        cl=[]
        el=self._layerManager.returnElements(self.currentProject())
        for i in el:
            
            if i.oclIsKindOf(metamodel.Classifier):
                ns=i.namespace
                inpp=None
                while not ns==self.currentProject():
                    if ns.name=='python2_2':
                        inpp=1
                        ns=self.currentProject()
                    else:
                        ns=ns.namespace
                if not inpp:
                    if i.oclIsKindOf(metamodel.Class) and not self._layerManager.isTestClass(i):
                        cl.append(i)
        
        for c in cl:
            ns=c.namespace
            while ns:
               
                fail=0
                undef=0
                for x in ns.ownedElement:

                    for y in x.taggedValue.select(lambda z: z.name=="correctness" ):
                        if y.dataValue=="some tests failed" or y.dataValue=="test failed":
                            fail=1
                    if not fail:
                        for y in x.taggedValue.select(lambda z: z.name=="correctness"):
                            if y.dataValue=="undefined":
                                undef=1
                    
                    
                for t in ns.taggedValue.select(lambda z: z.name=="correctness"):
                    if undef and not fail:
                        t.dataValue="undefined"
                    elif fail:
                        t.dataValue="some tests failed"
                    else:
                        t.dataValue="all tests passed"

                ns=ns.namespace
