# -*- coding: utf-8 -*-

# Copyright (c) 2004 - 2005 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the Python re wizard dialog.
"""

import sys
import os
import re

from qt import *

from KdeQt import KQMessageBox, KQInputDialog

from PyRegExpWizardForm import PyRegExpWizardForm

from PyRegExpWizardRepeatDialog import PyRegExpWizardRepeatDialog
from PyRegExpWizardCharactersDialog import PyRegExpWizardCharactersDialog

import UI.PixmapCache

class PyRegExpWizardDialog(PyRegExpWizardForm):
    """
    Class implementing the Python re wizard dialog.
    """
    def __init__(self,parent = None,fromEric = 1):
        """
        Constructor
        
        @param parent parent widget (QWidget)
        @param fromEric flag indicating a call from within eric3
        """
        PyRegExpWizardForm.__init__(self,parent,None,fromEric)
        
        # initialize icons of the tool buttons
        self.commentButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("comment.png")))
        self.charButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("characters.png")))
        self.anycharButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("anychar.png")))
        self.repeatButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("repeat.png")))
        self.nonGroupButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("nongroup.png")))
        self.groupButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("group.png")))
        self.namedGroupButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("namedgroup.png")))
        self.namedReferenceButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("namedreference.png")))
        self.altnButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("altn.png")))
        self.beglineButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("begline.png")))
        self.endlineButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("endline.png")))
        self.wordboundButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("wordboundary.png")))
        self.nonwordboundButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("nonwordboundary.png")))
        self.poslookaheadButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("poslookahead.png")))
        self.neglookaheadButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("neglookahead.png")))
        self.poslookbehindButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("poslookbehind.png")))
        self.neglookbehindButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("neglookbehind.png")))
        self.undoButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("editUndo.png")))
        self.redoButton.setIconSet(\
            QIconSet(UI.PixmapCache.getPixmap("editRedo.png")))
        
        self.resultTable.verticalHeader().hide()
        self.resultTable.setLeftMargin(0)
        self.resultTable.horizontalHeader().hide()
        self.resultTable.setTopMargin(0)
        
        if fromEric:
            self.copyButton.hide()
        else:
            self.variableLabel.hide()
            self.variableLineEdit.hide()
            self.importCheckBox.hide()
            self.variableLine.hide()
            self.cancelButton.hide()
            self.okButton.setText(self.trUtf8("&Quit"))
            
        self.namedGroups = re.compile(r"""\(?P<([^>]+)>""").findall

    def insertString(self, s, steps=0):
        """
        Private method to insert a string into line edit and move cursor.
        
        @param s string to be inserted into the regexp line edit
            (string or QString)
        @param steps number of characters to move the cursor (integer).
            Negative steps moves cursor back, positives forward.
        """
        self.regexpTextEdit.insert(s)
        if steps != 0:
            if steps < 0:
                act = QTextEdit.MoveBackward
                steps = abs(steps)
            else:
                act = QTextEdit.MoveForward
            for i in range(steps):
                self.regexpTextEdit.moveCursor(act, 0)
        
    def handleComment(self):
        """
        Private slot to handle the comment toolbutton.
        """
        self.insertString("(?#)", -1)
        
    def handleAnyChar(self):
        """
        Private slot to handle the any character toolbutton.
        """
        self.insertString(".")
        
    def handleNonGroup(self):
        """
        Private slot to handle the non group toolbutton.
        """
        self.insertString("(?:)", -1)
        
    def handleGroup(self):
        """
        Private slot to handle the group toolbutton.
        """
        self.insertString("()", -1)
        
    def handleNamedGroup(self):
        """
        Private slot to handle the named group toolbutton.
        """
        self.insertString("(?P<>)", -2)
        
    def handleNamedReference(self):
        """
        Private slot to handle the named reference toolbutton.
        """
        # determine cursor position as length into text
        para, index = self.regexpTextEdit.getCursorPosition()
        length = 0
        for i in range(para):
            length += self.regexpTextEdit.paragraphLength(i)
        length += index + self.regexpTextEdit.paragraphs() - 1
        
        # only present group names that occur before the current cursor position
        regex = unicode(self.regexpTextEdit.text().left(length))
        names = self.namedGroups(regex)
        if not names:
            KQMessageBox.information(None,
                self.trUtf8("Named reference"),
                self.trUtf8("""No named groups have been defined yet."""),
                self.trUtf8("&OK"),
                QString.null,
                QString.null,
                0, -1)
            return
            
        qs = QStringList()
        for name in names:
            qs.append(name)
        groupName, ok = KQInputDialog.getItem(\
            self.trUtf8("Named reference"),
            self.trUtf8("Select group name:"),
            qs,
            0, 0)
        if ok and not groupName.isEmpty():
            self.insertString("(?P=%s)" % groupName)
        
    def handleAlternatives(self):
        """
        Private slot to handle the alternatives toolbutton.
        """
        self.insertString("(|)", -2)
        
    def handleBeginLine(self):
        """
        Private slot to handle the begin line toolbutton.
        """
        self.insertString("^")
        
    def handleEndLine(self):
        """
        Private slot to handle the end line toolbutton.
        """
        self.insertString("$")
        
    def handleWordBoundary(self):
        """
        Private slot to handle the word boundary toolbutton.
        """
        self.insertString("\\b")
        
    def handleNonWordBoundary(self):
        """
        Private slot to handle the non word boundary toolbutton.
        """
        self.insertString("\\B")
        
    def handlePosLookahead(self):
        """
        Private slot to handle the positive lookahead toolbutton.
        """
        self.insertString("(?=)", -1)
        
    def handleNegLookahead(self):
        """
        Private slot to handle the negative lookahead toolbutton.
        """
        self.insertString("(?!)", -1)
        
    def handlePosLookbehind(self):
        """
        Private slot to handle the positive lookbehind toolbutton.
        """
        self.insertString("(?<=)", -1)
        
    def handleNegLookbehind(self):
        """
        Private slot to handle the negative lookbehind toolbutton.
        """
        self.insertString("(?<!)", -1)
        
    def handleRepeat(self):
        """
        Private slot to handle the repeat toolbutton.
        """
        dlg = PyRegExpWizardRepeatDialog(self)
        if dlg.exec_loop() == QDialog.Accepted:
            self.insertString(dlg.getRepeat())
        
    def handleCharacters(self):
        """
        Private slot to handle the characters toolbutton.
        """
        dlg = PyRegExpWizardCharactersDialog(self)
        if dlg.exec_loop() == QDialog.Accepted:
            self.insertString(dlg.getCharacters())
    
    def copy(self):
        """
        Private slot to copy the regexp string into the clipboard.
        
        This slot is only available, if not called from within eric3.
        """
        escaped = self.regexpTextEdit.text()
        if not escaped.isEmpty():
            escaped = escaped.replace("\\", "\\\\")
            cb = QApplication.clipboard()
            cb.setText(escaped, QClipboard.Clipboard)
            if cb.supportsSelection():
                cb.setText(escaped, QClipboard.Selection)

    def validate(self):
        """
        Private slot to validate the entered regexp.
        """
        regex = self.regexpTextEdit.text()
        if not regex.isEmpty():
            try:
                regobj = re.compile(unicode(regex),
                    not self.caseSensitiveCheckBox.isChecked() and re.IGNORECASE or 0 | \
                    self.multilineCheckBox.isChecked() and re.MULTILINE or 0 | \
                    self.dotallCheckBox.isChecked() and re.DOTALL or 0 | \
                    self.verboseCheckBox.isChecked() and re.VERBOSE or 0 | \
                    self.localeCheckBox.isChecked() and re.LOCALE or 0 | \
                    self.unicodeCheckBox.isChecked() and re.UNICODE or 0)
                KQMessageBox.information(None,
                    self.trUtf8(""),
                    self.trUtf8("""The regular expression is valid."""),
                    self.trUtf8("&OK"),
                    QString.null,
                    QString.null,
                    0, -1)
            except re.error, e:
                KQMessageBox.critical(None,
                    self.trUtf8("Error"),
                    self.trUtf8("""Invalid regular expression: %1""")
                        .arg(unicode(e)),
                    self.trUtf8("&OK"),
                    QString.null,
                    QString.null,
                    0, -1)
                return
            except IndexError:
                KQMessageBox.critical(None,
                    self.trUtf8("Error"),
                    self.trUtf8("""Invalid regular expression: missing group name"""),
                    self.trUtf8("&OK"),
                    QString.null,
                    QString.null,
                    0, -1)
                return
        else:
            KQMessageBox.critical(None,
                self.trUtf8("Error"),
                self.trUtf8("""A regular expression must be given."""),
                self.trUtf8("&OK"),
                QString.null,
                QString.null,
                0, -1)

    def execute(self):
        """
        Private slot to execute the entered regexp on the test text.
        
        This slot will execute the entered regexp on the entered test
        data and will display the result in the table part of the dialog.
        """
        regex = self.regexpTextEdit.text()
        text = self.textTextEdit.text()
        if not regex.isEmpty() and not text.isEmpty():
            try:
                regobj = re.compile(unicode(regex),
                    not self.caseSensitiveCheckBox.isChecked() and re.IGNORECASE or 0 | \
                    self.multilineCheckBox.isChecked() and re.MULTILINE or 0 | \
                    self.dotallCheckBox.isChecked() and re.DOTALL or 0 | \
                    self.verboseCheckBox.isChecked() and re.VERBOSE or 0 | \
                    self.localeCheckBox.isChecked() and re.LOCALE or 0 | \
                    self.unicodeCheckBox.isChecked() and re.UNICODE or 0)
                matchobj = regobj.search(unicode(text))
                if matchobj is not None:
                    captures = matchobj.lastindex
                    if captures is None:
                        captures = 0
                else:
                    captures = 0
                row = 0
                OFFSET = 5
                
                self.resultTable.setNumRows(0)
                self.resultTable.setNumRows(captures + OFFSET)
                self.resultTable.setText(row, 0, self.trUtf8("Regexp"))
                self.resultTable.setText(row, 1, regex)
                self.resultTable.item(row, 1).setSpan(1, 2)
                
                if matchobj is not None:
                    row += 1
                    self.resultTable.setText(row, 0, self.trUtf8("Offset"))
                    self.resultTable.setText(row, 1, QString.number(matchobj.start(0)))
                    self.resultTable.item(row, 1).setSpan(1, 2)
                    
                    row += 1
                    self.resultTable.setText(row, 0, self.trUtf8("Captures"))
                    self.resultTable.setText(row, 1, QString.number(captures))
                    self.resultTable.item(row, 1).setSpan(1, 2)
                    row += 1
                    self.resultTable.setText(row, 1, self.trUtf8("Text"))
                    self.resultTable.setText(row, 2, self.trUtf8("Characters"))
                        
                    row += 1
                    self.resultTable.setText(row, 0, self.trUtf8("Match"))
                    self.resultTable.setText(row, 1, matchobj.group(0))
                    self.resultTable.setText(row, 2, QString.number(len(matchobj.group(0))))
                    
                    for i in range(1, captures + 1):
                        self.resultTable.setText(row + i, 0, self.trUtf8("Capture #%1").arg(i))
                        self.resultTable.setText(row + i, 1, matchobj.group(i))
                        self.resultTable.setText(row + i, 2, QString.number(len(matchobj.group(i))))
                else:
                    self.resultTable.setNumRows(2)
                    row += 1
                    self.resultTable.setText(row, 0, self.trUtf8("No matches"))
                    self.resultTable.item(row, 0).setSpan(1, 3)
                    
                for i in range(self.resultTable.numCols()):
                    self.resultTable.adjustColumn(i)
                for i in range(self.resultTable.numRows()):
                    self.resultTable.adjustRow(i)
            except re.error, e:
                KQMessageBox.critical(None,
                    self.trUtf8("Error"),
                    self.trUtf8("""Invalid regular expression: %1""")
                        .arg(unicode(e)),
                    self.trUtf8("&OK"),
                    QString.null,
                    QString.null,
                    0, -1)
                return
            except IndexError:
                KQMessageBox.critical(None,
                    self.trUtf8("Error"),
                    self.trUtf8("""Invalid regular expression: missing group name"""),
                    self.trUtf8("&OK"),
                    QString.null,
                    QString.null,
                    0, -1)
                return
        else:
            KQMessageBox.critical(None,
                self.trUtf8("Error"),
                self.trUtf8("""A regular expression and a text must be given."""),
                self.trUtf8("&OK"),
                QString.null,
                QString.null,
                0, -1)
        
    def getCode(self, indLevel, indString):
        """
        Public method to get the source code.
        
        @param indLevel indentation level (int)
        @param indString string used for indentation (space or tab) (string)
        @return generated code (string)
        """
        # calculate the indentation string
        istring = indLevel * indString
        i1string = (indLevel + 1) * indString
        
        # now generate the code
        reVar = str(self.variableLineEdit.text())
        if not reVar:
            reVar = "regexp"
            
        regexp = unicode(self.regexpTextEdit.text())
        
        flags = []
        if not self.caseSensitiveCheckBox.isChecked():
            flags.append('re.IGNORECASE')
        if self.multilineCheckBox.isChecked():
            flags.append('re.MULTILINE')
        if self.dotallCheckBox.isChecked():
            flags.append('re.DOTALL')
        if self.verboseCheckBox.isChecked():
            flags.append('re.VERBOSE')
        if self.localeCheckBox.isChecked():
            flags.append('re.LOCALE')
        if self.unicodeCheckBox.isChecked():
            flags.append('re.UNICODE')
        flags = " | ".join(flags)
        
        code = ''
        if self.importCheckBox.isChecked():
            code += 'import re%s%s' % (os.linesep, istring)
        code += '%s = re.compile(r"""%s"""' % \
            (reVar, regexp.replace('"', '\\"'))
        if flags:
            code += ', \\%s%s%s' % (os.linesep, i1string, flags)
        code += ')%s' % os.linesep
        return code
