#!/usr/bin/env python
#############################################################################
#
#  Linux Desktop Testing Project http://ldtp.freedesktop.org
# 
#  Author:
#     Nagappan Alagappan <nagappan@gmail.com>
#     Thanumalayan <madthanu@gmail.com>
#     Vinod Kumar <vinod.gre@gmail.com>
# 
#  Copyright 2008 - 2009 Nagappan Alagappan
# 
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Library General Public
#  License as published by the Free Software Foundation; either
#  version 2 of the License, or (at your option) any later version.
# 
#  This library is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  Library General Public License for more details.
# 
#  You should have received a copy of the GNU Library General Public
#  License along with this library; if not, write to the
#  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
#  Boston, MA 02110, USA.
#
#############################################################################

__author__ = "Nagappan Alagappan <nagappan@gmail.com>, Thanumalayan <madthanu@gmail.com>, Vinod Kumar <vinod.gre@gmail.com>"
# __maintainer__ = "Nagappan Alagappan <nagappan@gmail.com>"
__version__ = "1.7.0"

import os
import re
import sys
import thread
import traceback
from xml.sax import saxutils
from xml.parsers.expat import ExpatError
from xml.dom.minidom import parse, parseString

# Let us not register our application under at-spi application list
os.environ ['GTK_MODULES'] = ''
_ldtpDebug = os.getenv ('LDTP_DEBUG')

try:
    import pygtk
    # tell pyGTK, if possible, that we want GTKv2
    pygtk.require ("2.0")
except:
    # Some distributions come with GTK2, but not pyGTK
    pass
try:
    import gtk
    import gnome
    import gobject
    import gtk.glade
    import gtk.keysyms
except:
    print "You need to install pyGTK or GTKv2 ",
    print "or set your PYTHONPATH correctly."
    print "try: export PYTHONPATH=",
    print "/usr/lib/python/site-packages/"
    sys.exit (1)

isGtkSourceView = False
try:
    import gtksourceview
    isGtkSourceView = True
except:
    if _ldtpDebug:
        if hasattr (traceback, 'format_exc'):
            print traceback.format_exc ()
        else:
            print traceback.print_exc ()

try:
	import ldtplib.libldtpcodegen
except:
    if hasattr (traceback, 'format_exc'):
        print traceback.format_exc ()
    else:
        print traceback.print_exc ()
    raise ImportError,  'libldtpcodegen not found'

ldtplib.libldtpcodegen.calledFromGui = True

def getSourceView (sourceType):
    sourceManager = gtksourceview.SourceLanguagesManager ()
    sourceLang = sourceManager.get_language_from_mime_type (sourceType)
    buf = gtksourceview.SourceBuffer ()
    sourceView = gtksourceview.SourceView (buf)
    buf.set_language (sourceLang)
    buf.set_highlight (True)
    return sourceView

class LdtpEditorGui (gnome.Program):
    def __init__ (self):
        """
        In this init we are going to display the main
        serverinfo window
        """
        gnome.Program.__init__ (self)
        ldtpEditorApp = gnome.program_init ('LDTP Editor', '1.0.0')
        gladeFileName = "ldtpeditor.glade"
        # On install the following line will be replaced with glade path
        self.gladeFilePath = None
        if self.gladeFilePath:
            self.gladeFilePath = '%s/./%s' % (self.gladeFilePath,
                                              gladeFileName)
        else:
            self.gladeFilePath = './%s' % gladeFileName
        if os.path.exists (gladeFileName):
            self.gladeFilePath = sys.path [0] + '/' + gladeFileName
        else:
            self.gladeFilePath = '/usr/share/ldtp/glade/' + gladeFileName
            if os.path.exists (self.gladeFilePath) is False:
                self.gladeFilePath = '/usr/share/local/ldtp/glade/' + gladeFileName
        self.wTree = gtk.glade.XML (self.gladeFilePath)

        dic = { "on_quit_activate" : self.quittingApplication,
                "on_ldtpeditor_destroy_event" : self.quittingApplication,
                "on_preference_clicked" : self.preferenceClicked,
		        "on_appListEdit_clicked" : self.appListEditClicked,
                "on_about_clicked" : self.aboutClicked,
                "on_convert_clicked" : self.convertClicked,
                "on_save_clicked" : self.saveClicked,
                "on_startstop_clicked" : self.recordClicked,
                "on_play_clicked" : self.playClicked}
        self.wTree.signal_autoconnect (dic)
        self.txtGeneratedCode = self.wTree.get_widget ("txtGeneratedCode")
        self.txtGeneratedCode.set_editable (False)
        self.txtGeneratedXml = self.wTree.get_widget ("txtGeneratedXml")
        self.txtGeneratedXml.set_editable (False)
        self.wTree.get_widget ("chkListenKeyEvents").set_active (True)
        self.wTree.get_widget ("chkListenMouseEvents").set_active (True)
        self.wTree.get_widget ("chkGenerateLdtpCode").set_active (True)
        self.chkGenerateDataXml   = False
        self.recording = False
        self.fileName = None
        self.btnRecord = self.wTree.get_widget ("btnStartStop")
        self.btnRecord.set_label ('Start')
        self.btnPlay = self.wTree.get_widget ("btnPlay")
        self.btnPlay.set_sensitive (False)
        self.btnSave = self.wTree.get_widget ("btnSave")
        self.btnSave.set_sensitive (False)
        self.btnConvert = self.wTree.get_widget ("btnConvert")
        self.btnConvert.set_sensitive (False)
        self.txtRecordedCode = self.wTree.get_widget ("txtRecordedCode")
        self.txtRecordedCode.set_editable (False)
        if isGtkSourceView:
            parent = self.txtRecordedCode.get_parent ()
            parent.remove (self.txtRecordedCode)
            txtView = getSourceView ('text/x-python')
            parent.add (txtView)
            txtView.show ()
            self.txtRecordedCode = txtView
            parent = self.txtGeneratedCode.get_parent ()
            parent.remove (self.txtGeneratedCode)
            txtCodeView = getSourceView ('text/x-python')
            parent.add (txtCodeView)
            txtCodeView.show ()
            self.txtGeneratedCode = txtCodeView
            parent = self.txtGeneratedXml.get_parent ()
            parent.remove (self.txtGeneratedXml)
            txtXmlView = getSourceView ('text/xml')
            parent.add (txtXmlView)
            txtXmlView.show ()
            self.txtGeneratedXml = txtXmlView
        self.recordedCodeView = self.txtRecordedCode.get_buffer ()
        self.recordedCodeView.set_text ('')
        self.generatedCodeView = self.txtGeneratedCode.get_buffer ()
        self.generatedCodeView.set_text ('')
        self.generatedXmlView = self.txtGeneratedXml.get_buffer ()
        self.generatedXmlView.set_text ('')
        self.recordedCode = ''
        self.txtPlayOutput = self.wTree.get_widget ("txtPlayOutput")
        self.txtPlayOutput.set_editable (False)
        self.txtPlayOutputView = self.txtPlayOutput.get_buffer ()

    ##### CALLBACKS

    def quittingApplication (self, widget):
        ldtplib.libldtpcodegen.stop ()
        gtk.main_quit (self, widget)

    def recordClicked (self, widget):
        count = 0
        if self.recording == False:
            self.fileName = None
            self.recording = True
            self.btnRecord.set_label ('Stop')
            self.recordedCodeView.set_text ('')
            self.generatedCodeView.set_text ('')
            self.generatedXmlView.set_text ('')
            global app
            ldtplib.libldtpcodegen.start (app.callbackFunc)
        else:
            ldtplib.libldtpcodegen.stop ()
            self.recording = False
            self.btnRecord.set_label ('Start')
            count = self.recordedCodeView.get_char_count ()
        if count:
            self.txtGeneratedCode.set_editable (True)
            self.txtGeneratedXml.set_editable (True)
            self.txtRecordedCode.set_editable (True)
            self.txtRecordedCode.set_cursor_visible (True)
            self.btnPlay.set_sensitive (True)
            self.btnSave.set_sensitive (True)
            self.btnConvert.set_sensitive (True)

    def playClicked (self, widget):
        start,  end = self.generatedCodeView.get_bounds ()
        generatedCode = ''
        if end == start:
            start, end = self.recordedCodeView.get_bounds ()
            generatedCode = self.recordedCodeView.get_text (start, end)
        else:
            generatedCode = self.generatedCodeView.get_text (start, end)
    
        thread.start_new_thread (self.playback, (generatedCode, ))

    def saveClicked (self, widget):
        if self.fileName is None:
            self.saveAs ()
        else:
            self.saveFile ()
        
    def saveFile (self):
        start,  end = self.generatedCodeView.get_bounds ()
        txt2Copy = ''
        if end == start:
            start, end = self.recordedCodeView.get_bounds ()
            txt2Copy = self.recordedCodeView.get_text (start, end)
        else:
            txt2Copy = self.generatedCodeView.get_text (start, end)
        error_dialog = None

        try:
            file (self.fileName, "w"). write (txt2Copy)
        except IOError, ex:
            error_dialog = gtk.MessageDialog (self.wTree,
                                             gtk.DIALOG_DESTROY_WITH_PARENT,
                                             gtk.MESSAGE_ERROR,
                                             gtk.BUTTONS_CLOSE,
                                             "Error saving to file %s:\n%s" %
                                             (open_filename,
                                              str (ex)))
            error_dialog.connect ("response", gtk.Widget.destroy)
            error_dialog.show ()

    def saveAs (self):
        dialog = gtk.FileChooserDialog ("Select file",
                                        self.wTree.get_widget ('LDTPEditor'),
                                        gtk.FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME,
                                        (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                        gtk.STOCK_SAVE, gtk.RESPONSE_OK))
        dialog.set_default_response (gtk.RESPONSE_OK)
        response = dialog.run ()

        if response == gtk.RESPONSE_OK:
            self.fileName = dialog.get_filename ()
            self.saveFile ()
  
        dialog.destroy()

    def preferenceClicked (self, widget):
        self.dlgPreferences = self.wTree.get_widget ("dlgPreferences")
        pref = ldtplib.libldtpcodegen.Prefs ()
        prefDic = pref.loadPrefs ()
        for param, value in prefDic.iteritems ():
            widget = "chk%s" % param
            self.wTree.get_widget (widget).set_active (value)
        response = self.dlgPreferences.run ()
        if response == gtk.RESPONSE_OK:
            self.chkListenKeyEvents   = self.wTree.get_widget ("chkListenKeyEvents").get_active ()
            self.chkListenMouseEvents = self.wTree.get_widget ("chkListenMouseEvents").get_active ()
            self.chkGenerateLdtpCode  = self.wTree.get_widget ("chkGenerateLdtpCode").get_active ()
            self.chkGenerateDataXml   = self.wTree.get_widget ("chkGenerateDataXml").get_active ()
            self.chkGenerateKeyEvents = self.wTree.get_widget ("chkGenerateKeyEvents").get_active ()
            self.chkGenerateWaitTime  = self.wTree.get_widget ("chkGenerateWaitTime").get_active ()
            self.chkGenerateMemCpuStat = self.wTree.get_widget ("chkGenerateMemCpuStat").get_active ()
            self.chkGenerateOoldtp = self.wTree.get_widget ("chkGenerateOoldtp").get_active ()
            prefDic ['ListenKeyEvents'] = self.chkListenKeyEvents
            prefDic ['ListenMouseEvents'] = self.chkListenMouseEvents
            prefDic ['GenerateLdtpCode'] = self.chkGenerateLdtpCode
            prefDic ['GenerateDataXml'] = self.chkGenerateDataXml
            prefDic ['GenerateKeyEvents'] = self.chkGenerateKeyEvents
            prefDic ['GenerateWaitTime'] = self.chkGenerateWaitTime
            prefDic ['GenerateMemCpuStat'] = self.chkGenerateMemCpuStat
            prefDic ['GenerateOoldtp'] = self.chkGenerateOoldtp
            pref.savePrefDic (prefDic)
        self.dlgPreferences.hide ()

    def appListEditClicked (self, widget):
        appDlg = appListEdit (self.gladeFilePath)
        result, newAppList = appDlg.run ()
        if result == gtk.RESPONSE_OK:
            ldtplib.libldtpcodegen.saveAppList (newAppList)

    def aboutClicked (self, widget):
        self.dlgAbout = self.wTree.get_widget ("dlgAbout")
        response = self.dlgAbout.run ()
        if response == gtk.RESPONSE_OK:
            pass
        self.dlgAbout.hide ()

    def convertClicked (self, widget):
        start, end = self.recordedCodeView.get_bounds ()
        self.recordedCode = self.recordedCodeView.get_text (start, end, False)
        if self.recordedCode != '':
            pref = ldtplib.libldtpcodegen.Prefs ()
            prefDic = pref.loadPrefs ()
            if prefDic ['GenerateLdtpCode'] == True:
                xml = ''
                lines = 'from ldtp import *\nfrom ldtputils import *\n\n'
                if prefDic ['GenerateOoldtp'] == True:
                    lines = '%sfrom ooldtp import *\n' % lines
                    ldtplib.libldtpcodegen.initContext ()
                if prefDic ['GenerateDataXml'] == True:
                    lines = '%sxmlParser = LdtpDataFileParser (datafilename)\n\n' % lines
                    xml = '%s<data>\n' % xml
                if prefDic ['GenerateMemCpuStat'] == True:
                    lines = '%sxstats = None\n' % lines
                lines = '%stry:\n' % lines
                if prefDic ['GenerateMemCpuStat'] == True:
                    lines = '%s\txstats = pstats (\"<INCLUDE YOUR APPLICATION NAME>\", 2)\n\txstats.start ()\n' % lines
                #lines += '\tlog (\"test script\", \"teststart\")\n\n'
                _lastWaitTillGuiExist = []
                _cmdRepeated = []
                for line in self.recordedCode.split ('\n'):
                    cmd = re.split ('\(',  line,  1)
                    if cmd != None:
                        if _cmdRepeated == []:
                            _cmdRepeated  = cmd
                        elif len (cmd) == 2 and len (_cmdRepeated) == 2 and \
                            _cmdRepeated [0] == cmd [0] and _cmdRepeated [1] == cmd [1]:
                            continue
                        else:
                            _cmdRepeated = cmd
                    if len (cmd) < 2:
                        continue
                    if prefDic ['GenerateKeyEvents'] == False:
                        if cmd != None and re.search ('enterstring',  cmd [0]) != None:
                            continue
                    if prefDic ['GenerateWaitTime'] == False:
                        if cmd != None:
                            match = re.match ('wait',  cmd [0])
                            if match != None and len (cmd [0]) <= 5:
                                continue
                    if cmd != None and re.search ('waittillguiexist',  cmd [0]) != None:
                        if _lastWaitTillGuiExist != [] and _lastWaitTillGuiExist [1] != cmd [1]:
                            _waitTillGuiExist = '%s(%s\n' % (_lastWaitTillGuiExist [0], _lastWaitTillGuiExist [1])
                            if prefDic ['GenerateOoldtp'] == True:
                                _waitTillGuiExist = ldtplib.libldtpcodegen.objectOrient (_waitTillGuiExist)
                            lines = '%s\t%s' % (lines, _waitTillGuiExist)
                        _lastWaitTillGuiExist = cmd
                        continue
                    if cmd != None and re.search ('waittillguinotexist',  cmd [0]) != None:
                        if _lastWaitTillGuiExist != [] and _lastWaitTillGuiExist [1] == cmd [1]:
                            _lastWaitTillGuiExist = []
                            continue
                    if _lastWaitTillGuiExist != []:
                        _waitTillGuiNotExist = '%s(%s\n' % (_lastWaitTillGuiExist [0], _lastWaitTillGuiExist [1])
                        if prefDic ['GenerateOoldtp'] == True:
                            _waitTillGuiNotExist = ldtplib.libldtpcodegen.objectOrient (_waitTillGuiNotExist)
                        lines = '%s\t%s' % (lines, _waitTillGuiNotExist)
                    _lastWaitTillGuiExist = []
                    if prefDic ['GenerateDataXml'] == True:
                        code= re.split (',', line, 2)
                        if len (code) > 2:
                            component = code [1][re.search ('\w+', code [1]).start () : len (code [1]) - 1]
                            data = code [2][re.search ('\w+', code [2]).start () : len (code [2]) - 2]
                            lines = '%s\t%s = xmlParser.gettagvalue (\"%s\")\n' \
                                    % (lines, component, component)
                            if prefDic ['GenerateOoldtp'] == True and line.strip() != '' \
                                    and re.match ('wait\s', line) == None:
                                lines = '%s\t%s\"%s\", %s [0])\n' \
                                        % (lines, ldtplib.libldtpcodegen.objectOrient (code [0]), component, component)
                            else:
                                lines = '%s\t%s, \"%s\", %s [0])\n' \
                                    % (lines, code [0], component, component) 
                            xml = '%s<%s>%s</%s>\n' % (xml, component, saxutils.escape (data), component)
                            continue
                    if prefDic ['GenerateOoldtp'] == True and line.strip() != '' and re.match ('wait\s', line) == None:
                        line = ldtplib.libldtpcodegen.objectOrient (line)
                    lines = '%s\t%s\n' % (lines, line)
                #lines += '\n\tlog (\"test script\", \"pass\")\n'
                #lines += '\tlog (\"test script\", \"testend\")\n'
                lines = '%sexcept LdtpExecutionError, msg:\n' % lines
                if prefDic ['GenerateMemCpuStat'] == True:
                    lines = '%s\tif xstats is not None:\n\t\txstats.stop ()\n' % lines
                #lines += '\tlog (str (msg), \"cause\")\n'
                #lines += '\tlog (\"test script\", \"fail\")\n'
                #lines += '\tlog (\"test script\", \"testend\")\n'
                lines = '%s\traise\n' % lines
                self.generatedCodeView.set_text (lines.encode ('utf-8'))
                if prefDic ['GenerateDataXml'] == True:
                    xml = '%s</data>\n' % xml
                    self.generatedXmlView.set_text (xml.encode ('utf-8'))

    def callbackFunc (self, recordedData):
        if self.recording == True:
            self.recordedCodeView.insert (self.recordedCodeView.get_end_iter (), recordedData)
        
    def playback (self, generatedLDTPScript):
        try:
            if generatedLDTPScript == None or generatedLDTPScript == '':
                raise ValueError ('No code to replay')
            exec (generatedLDTPScript)
            self.txtPlayOutputView.set_text ('Success\n')
        except:
            if hasattr (traceback, 'format_exc'):
                self.txtPlayOutputView.set_text ('Failure\n\n' + str (traceback.format_exc ()))
            else:
                self.txtPlayOutputView.set_text ('Failure\n\n' + str (traceback.print_exc ()))

class appListEdit:
    def __init__ (self, gladePath):
        self.gladeFilePath = gladePath
        self.wTree = gtk.glade.XML (self.gladeFilePath, "appDlg")
        dic = {"on_addApp" : self.addAppClicked,
               "on_deleteApp" : self.deleteAppClicked}
        self.wTree.signal_autoconnect (dic)
        self.appList = ldtplib.libldtpcodegen.getapps ()
        self.listView = self.wTree.get_widget ("listView")
        self.addColumn ("Window Name", 0)
        self.listModel = gtk.ListStore (str)
        self.listView.set_model (self.listModel)

    def addAppClicked (self, widget):
        addDlg = addApp (self.gladeFilePath)
        result, newApp = addDlg.run ()
        if result == gtk.RESPONSE_OK:
            self.appList.append (newApp)
            self.listModel.append ([newApp])

    def deleteAppClicked (self, widget):
        selection = self.listView.get_selection ()
        model, selection_iter = selection.get_selected ()
        if (selection_iter):
            app = self.listModel.get_value (selection_iter, 0)
            self.wTree = gtk.glade.XML (self.gladeFilePath, "deleteApp")
            self.deleteApp = self.wTree.get_widget ("deleteApp")
            response = self.deleteApp.run ()
            if response == gtk.RESPONSE_OK:
                self.appList.remove (app)
                self.listModel.remove (selection_iter)
            self.deleteApp.hide ()
    
    def addColumn (self, title, columnId):
        column = gtk.TreeViewColumn (title, gtk.CellRendererText(), text=columnId)
        column.set_resizable (True)
        column.set_sort_column_id (columnId)
        self.listView.append_column (column)
    
    def run (self):
        self.appDlg = self.wTree.get_widget ("appDlg")
        self.showApp ()
        self.result = self.appDlg.run ()
        self.newAppList = self.appList
        self.appDlg.destroy ()
        return self.result, self.newAppList

    def showApp (self):
        for item in self.appList:
            self.listModel.append ([item])

class addApp:
    def __init__ (self, gladePath):
        self.gladeFilePath = gladePath
        
    def run (self):
        self.wTree = gtk.glade.XML (self.gladeFilePath, "addApp")
        self.addApp = self.wTree.get_widget ("addApp")
        self.result = self.addApp.run ()
        self.enApp = self.wTree.get_widget ("enApp")
        self.newApp = self.enApp.get_text ()
        self.addApp.destroy ()
        return self.result, self.newApp

try:
    # we start the app like this...
    app = LdtpEditorGui ()
except RuntimeError:
    print 'Glade file not found'
    sys.exit (0)

try:
    gobject.threads_init ()
    gtk.gdk.threads_enter ()
    gtk.main ()
    gtk.gdk.threads_leave ()
except KeyboardInterrupt:
    pass
