#   Programmer: Daniel Pozmanter
#   E-mail:     drpython@bluebottle.com
#   Note:       You must reply to the verification e-mail to get through.
#
#   Copyright 2003-2007 Daniel Pozmanter
#
#   Distributed under the terms of the GPL (GNU Public License)
#
#    DrPython is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program 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 General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#Regular Expression Dialog

import re
import wx
import drScrolledMessageDialog
import drFileDialog

wildcard = "Text File (*.txt)|*.txt|All files (*)|*"

class drRETextCtrl(wx.TextCtrl):
    def __init__(self, parent, id, value, pos, size):
        wx.TextCtrl.__init__(self, parent, id, value, pos, size)

        self.Bind(wx.EVT_CHAR, self.OnChar)

    def OnChar(self, event):
        if event.GetKeyCode() == wx.WXK_ESCAPE:
            self.GetParent().OnbtnCancel(event)
        elif event.GetKeyCode() == wx.WXK_RETURN:
            self.GetParent().OnbtnOk(event)
        else:
            event.Skip()


class drRegularExpressionDialog(wx.Frame):
    def __init__(self, parent, id, title, prompthasfocus = 0, infiles = 0):
        if parent.PLATFORM_IS_WIN:
            size = wx.Size(500, 160)
        else:
            size = wx.Size(500, 110)

        wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, size, wx.DEFAULT_DIALOG_STYLE | wx.MAXIMIZE_BOX | wx.THICK_FRAME | wx.RESIZE_BORDER)

        self.ID_OK = 1001
        self.ID_CANCEL = 1002

        self.ID_LOAD = 1003
        self.ID_SAVE = 1004

        self.ID_ANYCHARACTER = 1010
        self.ID_ANYCHARACTERD = 10101
        self.ID_ANYCHARACTERND = 10102
        self.ID_ANYCHARACTERW = 10103
        self.ID_ANYCHARACTERNW = 10104
        self.ID_ANYCHARACTERA = 10105
        self.ID_ANYCHARACTERNA = 10106
        self.ID_SETOFCHARACTERS = 10107
        self.ID_START = 1011
        self.ID_END = 1012
        self.ID_STARTD = 1111
        self.ID_ENDD = 1112
        self.ID_EDGEW = 1211
        self.ID_EDGENW = 1212
        self.ID_REPSZEROPLUS = 1013
        self.ID_REPSONEPLUS = 1014
        self.ID_REPSZEROORONE = 1015
        self.ID_REPSN = 1016
        self.ID_GROUP = 1017
        self.ID_OR = 10171
        self.ID_POSITIVE_LOOKAHEAD = 1018
        self.ID_NEGATIVE_LOOKAHEAD = 1019
        self.ID_POSITIVE_LOOKBEHIND = 1118
        self.ID_NEGATIVE_LOOKBEHIND = 1119

        self.ID_INSERT_NORMAL_TEXT = 1050

        self.insert = (title == "Insert Regular Expression")

        self.theSizer = wx.FlexGridSizer(4, 1, 5, 10)

        okcancelSizer = wx.BoxSizer(wx.HORIZONTAL)

        self.parent = parent
        self.prompthasfocus = prompthasfocus

        if self.insert:
            self.defaultdirectory = self.parent.prefs.defaultdirectory
            self.enablefeedback = self.parent.prefs.enablefeedback
            self.filedialogparent = self.parent
        elif not infiles:
            self.defaultdirectory = self.parent.GetParent().prefs.defaultdirectory
            self.enablefeedback = self.parent.GetParent().prefs.enablefeedback
            self.filedialogparent = self.parent.GetParent()
        else:
            self.defaultdirectory = self.parent.GetGrandParent().prefs.defaultdirectory
            self.enablefeedback = self.parent.GetGrandParent().prefs.enablefeedback
            self.filedialogparent = self.parent.GetGrandParent()

        FileMenu = wx.Menu()
        FileMenu.Append(self.ID_LOAD, "&Load", " Load Regular Expression")
        FileMenu.Append(self.ID_SAVE, "&Save", " Save Regular Expression")

        TextMenu = wx.Menu()
        TextMenu.Append(self.ID_INSERT_NORMAL_TEXT, "Normal Text")
        TextMenu.Append(self.ID_ANYCHARACTER, "Any Character  \".\"")
        TextMenu.Append(self.ID_ANYCHARACTERD, "Any Decimal Digit  \"\\d\"")
        TextMenu.Append(self.ID_ANYCHARACTERND, "Any Non Digit  \"\\D\"")
        TextMenu.Append(self.ID_ANYCHARACTERW, "Any Whitespace Character  \"\\s\"")
        TextMenu.Append(self.ID_ANYCHARACTERNW, "Any Non Whitespace Character  \"\\S\"")
        TextMenu.Append(self.ID_ANYCHARACTERA, "Any AlphaNumeric Character  \"\\w\"")
        TextMenu.Append(self.ID_ANYCHARACTERNA, "Any Non AlphaNumeric Character  \"\\W\"")
        TextMenu.Append(self.ID_SETOFCHARACTERS, "A Set of Characters  \"[ ]\"")

        RepetitionsMenu = wx.Menu()
        RepetitionsMenu.Append(self.ID_REPSZEROPLUS, "0 Or More  \"*\"")
        RepetitionsMenu.Append(self.ID_REPSONEPLUS, "1 Or More  \"+\"")
        RepetitionsMenu.Append(self.ID_REPSZEROORONE, "0 Or 1  \"?\"")
        RepetitionsMenu.Append(self.ID_REPSN, "n  \"{n}\"")

        LimitMenu = wx.Menu()
        LimitMenu.Append(self.ID_START, "The Start Of Each Line  \"^\"")
        LimitMenu.Append(self.ID_END, "The End Of Each Line  \"$\"")
        LimitMenu.Append(self.ID_STARTD, "The Start of the Document  \"\\A\"")
        LimitMenu.Append(self.ID_ENDD, "The End of the Document  \"\\Z\"")
        LimitMenu.Append(self.ID_EDGEW, "The Start or End of a Word  \"\\b\"")
        LimitMenu.Append(self.ID_EDGENW, "Text That is Not at Either End of a Word  \"\\B\"")

        lookMenu = wx.Menu()
        lookMenu.Append(self.ID_POSITIVE_LOOKAHEAD, "Lookahead: Positive  \"(?=)\"")
        lookMenu.Append(self.ID_NEGATIVE_LOOKAHEAD, "Lookahead: Negative  \"(?!)\"")
        lookMenu.Append(self.ID_POSITIVE_LOOKBEHIND, "Lookbehind: Positive  \"(?<=)\"")
        lookMenu.Append(self.ID_NEGATIVE_LOOKBEHIND, "Lookbehind: Negative  \"(?<!)\"")

        InsertMenu = wx.Menu()
        InsertMenu.AppendMenu(3001, "&Text", TextMenu)
        InsertMenu.AppendMenu(3002, "&Repetitions", RepetitionsMenu)
        InsertMenu.AppendMenu(3003, "&Match", LimitMenu)
        InsertMenu.AppendMenu(3004, "&Assertions", lookMenu)
        InsertMenu.Append(self.ID_OR, "&Or  \"|\"")
        InsertMenu.Append(self.ID_GROUP, "&Group  \"( )\"")

        menuBar = wx.MenuBar()
        menuBar.Append(FileMenu,"&File")
        menuBar.Append(InsertMenu,"&Insert")

        self.SetMenuBar(menuBar)

        self.txtRE = drRETextCtrl(self, -1, "", wx.DefaultPosition, wx.Size(500, -1))

        self.btnOk = wx.Button(self, self.ID_OK, "&Ok")
        self.btnCancel = wx.Button(self, self.ID_CANCEL, "&Cancel")

        okcancelSizer.Add(self.btnOk, 1, wx.SHAPED)
        okcancelSizer.Add(self.btnCancel, 1, wx.SHAPED)

        self.theSizer.Add(self.txtRE, 1, wx.SHAPED | wx.ALIGN_CENTER)
        self.theSizer.Add(wx.StaticText(self, -1, "   "), 1, wx.SHAPED | wx.ALIGN_CENTER)
        self.theSizer.Add(okcancelSizer, 1, wx.SHAPED | wx.ALIGN_CENTER)

        self.SetAutoLayout(True)
        self.SetSizer(self.theSizer)

        self.btnOk.SetDefault()
        self.txtRE.SetFocus()

        self.Bind(wx.EVT_BUTTON,  self.OnbtnCancel, id=self.ID_CANCEL)
        self.Bind(wx.EVT_BUTTON,  self.OnbtnOk, id=self.ID_OK)

        self.Bind(wx.EVT_MENU,  self.OnLoad, id=self.ID_LOAD)
        self.Bind(wx.EVT_MENU,  self.OnSave, id=self.ID_SAVE)
        self.Bind(wx.EVT_MENU,  self.OnbtnAnyCharacter, id=self.ID_ANYCHARACTER)
        self.Bind(wx.EVT_MENU,  self.OnbtnAnyCharacterD, id=self.ID_ANYCHARACTERD)
        self.Bind(wx.EVT_MENU,  self.OnbtnAnyCharacterND, id=self.ID_ANYCHARACTERND)
        self.Bind(wx.EVT_MENU,  self.OnbtnAnyCharacterW, id=self.ID_ANYCHARACTERW)
        self.Bind(wx.EVT_MENU,  self.OnbtnAnyCharacterNW, id=self.ID_ANYCHARACTERNW)
        self.Bind(wx.EVT_MENU,  self.OnbtnAnyCharacterA, id=self.ID_ANYCHARACTERA)
        self.Bind(wx.EVT_MENU,  self.OnbtnAnyCharacterNA, id=self.ID_ANYCHARACTERNA)
        self.Bind(wx.EVT_MENU,  self.OnbtnSetOfCharacters, id=self.ID_SETOFCHARACTERS)
        self.Bind(wx.EVT_MENU,  self.OnbtnStart, id=self.ID_START)
        self.Bind(wx.EVT_MENU,  self.OnbtnEnd, id=self.ID_END)
        self.Bind(wx.EVT_MENU,  self.OnbtnStartD, id=self.ID_STARTD)
        self.Bind(wx.EVT_MENU,  self.OnbtnEndD, id=self.ID_ENDD)
        self.Bind(wx.EVT_MENU,  self.OnbtnEdgeW, id=self.ID_EDGEW)
        self.Bind(wx.EVT_MENU,  self.OnbtnEdgeNW, id=self.ID_EDGENW)
        self.Bind(wx.EVT_MENU,  self.OnbtnRepsZeroPlus, id=self.ID_REPSZEROPLUS)
        self.Bind(wx.EVT_MENU,  self.OnbtnRepsOnePlus, id=self.ID_REPSONEPLUS)
        self.Bind(wx.EVT_MENU,  self.OnbtnRepsZeroOrOne, id=self.ID_REPSZEROORONE)
        self.Bind(wx.EVT_MENU,  self.OnbtnRepsN, id=self.ID_REPSN)
        self.Bind(wx.EVT_MENU,  self.OnbtnOr, id=self.ID_OR)
        self.Bind(wx.EVT_MENU,  self.OnbtnGroup, id=self.ID_GROUP)
        self.Bind(wx.EVT_MENU,  self.OnbtnLookPositiveA, id=self.ID_POSITIVE_LOOKAHEAD)
        self.Bind(wx.EVT_MENU,  self.OnbtnLookNegativeA, id=self.ID_NEGATIVE_LOOKAHEAD)
        self.Bind(wx.EVT_MENU,  self.OnbtnLookPositiveB, id=self.ID_POSITIVE_LOOKBEHIND)
        self.Bind(wx.EVT_MENU,  self.OnbtnLookNegativeB, id=self.ID_NEGATIVE_LOOKBEHIND)

        self.Bind(wx.EVT_MENU,  self.OnbtnInsertNormalText, id=self.ID_INSERT_NORMAL_TEXT)

    def insertText(self, text):
        pos = self.txtRE.GetInsertionPoint()
        textRE = self.txtRE.GetValue()
        self.txtRE.SetValue(textRE[0:pos] + text + textRE[pos:])
        self.txtRE.SetInsertionPoint(pos + len(text))

    def OnbtnAnyCharacter(self, event):
        self.insertText('.')

    def OnbtnAnyCharacterA(self, event):
        self.insertText('\\w')

    def OnbtnAnyCharacterD(self, event):
        self.insertText('\\d')

    def OnbtnAnyCharacterNA(self, event):
        self.insertText('\\W')

    def OnbtnAnyCharacterND(self, event):
        self.insertText('\\D')

    def OnbtnAnyCharacterNW(self, event):
        self.insertText('\\S')

    def OnbtnAnyCharacterW(self, event):
        self.insertText('\\s')

    def OnbtnCancel(self, event):
        self.txtRE.SetValue("")
        self.Close(1)

    def OnbtnEdgeNW(self, event):
        self.insertText('\\B')

    def OnbtnEdgeW(self, event):
        self.insertText('\\b')

    def OnbtnEnd(self, event):
        self.insertText('$')

    def OnbtnEndD(self, event):
        self.insertText('\\Z')

    def OnbtnGroup(self, event):
        self.insertText('()')
        pos = self.txtRE.GetInsertionPoint()
        self.txtRE.SetInsertionPoint(pos - 1)

    def OnbtnInsertNormalText(self, event):
        d = wx.TextEntryDialog(self, "Enter Normal Text", "Insert Normal Text", "")
        answer = d.ShowModal()
        v = d.GetValue()
        d.Destroy()
        if answer == wx.ID_OK:
            self.insertText(re.escape(v))

    def OnbtnLookNegativeA(self, event):
        self.insertText('(?!)')
        pos = self.txtRE.GetInsertionPoint()
        self.txtRE.SetInsertionPoint(pos - 1)

    def OnbtnLookPositiveA(self, event):
        self.insertText('(?=)')
        pos = self.txtRE.GetInsertionPoint()
        self.txtRE.SetInsertionPoint(pos - 1)

    def OnbtnLookNegativeB(self, event):
        self.insertText('(?<!)')
        pos = self.txtRE.GetInsertionPoint()
        self.txtRE.SetInsertionPoint(pos - 1)

    def OnbtnLookPositiveB(self, event):
        self.insertText('(?<=)')
        pos = self.txtRE.GetInsertionPoint()
        self.txtRE.SetInsertionPoint(pos - 1)

    def OnbtnOk(self, event):
        self.Show(0)

        result = self.txtRE.GetValue()
        l = len(result)
        if l > 0:
            if self.insert:
                if self.prompthasfocus:
                    pos = self.parent.txtPrompt.GetCurrentPos()
                    self.parent.txtPrompt.InsertText(pos, result)
                    self.parent.txtPrompt.GotoPos(pos + l)
                else:
                    pos = self.parent.txtDocument.GetCurrentPos()
                    self.parent.txtDocument.InsertText(pos, result)
                    self.parent.txtDocument.GotoPos(pos + l)
            else:
                self.parent.txtSearchFor.SetValue(result)

        self.Close(1)

    def OnbtnOr(self, event):
        self.insertText('|')

    def OnbtnRepsN(self, event):
        d = wx.TextEntryDialog(self, "Enter The Desired Number of Repetitions:", "Insert N Repetitions", "")
        answer = d.ShowModal()
        v = d.GetValue()
        d.Destroy()
        if answer == wx.ID_OK:
            self.insertText('{' + v + '}')

    def OnbtnRepsOnePlus(self, event):
        self.insertText('+')

    def OnbtnRepsZeroOrOne(self, event):
        self.insertText('?')

    def OnbtnRepsZeroPlus(self, event):
        self.insertText('*')

    def OnbtnSetOfCharacters(self, event):
        self.insertText('[]')
        pos = self.txtRE.GetInsertionPoint()
        self.txtRE.SetInsertionPoint(pos - 1)

    def OnbtnStart(self, event):
        self.insertText('^')

    def OnbtnStartD(self, event):
        self.insertText('\\A')

    def OnLoad(self, event):
        dlg = drFileDialog.FileDialog(self.filedialogparent, "Load Regular Expression From", wildcard)
        if self.defaultdirectory:
            try:
                dlg.SetDirectory(self.defaultdirectory)
            except:
                drScrolledMessageDialog.ShowMessage(self, ("Error Setting Default Directory To: " + self.defaultdirectory), "DrPython Error")
        if dlg.ShowModal() == wx.ID_OK:
            refile = dlg.GetPath().replace("\\", "/")
            try:
                f = file(refile, 'r')
                text = f.read()
                f.close()
            except:
                drScrolledMessageDialog.ShowMessage(self, ("Error Reading From: " +  refile), "DrPython Error")
                text = ""
            if (text.find('\n') > -1) or (text.find('\r') > -1):
                drScrolledMessageDialog.ShowMessage(self, ("Error Reading From: " +  refile), "DrPython Error")
                text = ""
            self.txtRE.SetValue(text)

        dlg.Destroy()
        self.Raise()

    def OnSave(self, event):
        dlg = drFileDialog.FileDialog(self.filedialogparent, "Save Regular Expression As", wildcard, IsASaveDialog=True)
        if self.defaultdirectory:
            try:
                dlg.SetDirectory(self.defaultdirectory)
            except:
                drScrolledMessageDialog.ShowMessage(self, ("Error Setting Default Directory To: " + self.defaultdirectory), "DrPython Error")
        if dlg.ShowModal() == wx.ID_OK:
            refile = dlg.GetPath().replace("\\", "/")
            try:
                f = file(refile, 'w')
                f.write(self.txtRE.GetValue())
                f.close()
            except:
                drScrolledMessageDialog.ShowMessage(self, ("Error Writing To: " +  refile), "DrPython Error")
                return
            if self.enablefeedback:
                drScrolledMessageDialog.ShowMessage(self, ("Successfully Saved: " + refile), "Save Success")
            dlg.Destroy()
        self.Raise()
