#   Programmer:  Franz Steinhaeusler
#   E-mail:      francescoa@users.sourceforge.net
#   Note:        Initial Release 12/11/2004
#
#   Copyright 2004-2005 Franz Steinhaeusler
#
#   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

#Plugin
#Abbreviations

#Version: 0.0.0, 12/10/2004
#Version: 0.0.1, 12/14/2004

#this is needed for PyChecker
import sys
sys.path.append('c:/Eigene Dateien/python/drpython')

import wx
import os
import drPrefsFile
import re
import drScrolledMessageDialog

def OnAbout(DrFrame):
    Abbreviations_Version = "0.0.2"
    NameAndVersion = "Abbreviations:\n\nVersion: " + Abbreviations_Version + "\n"
    AboutString = NameAndVersion + "By Franz Steinhaeusler\n\nReleased under the GPL."
    DrFrame.ShowMessage(AboutString, "About")

def OnHelp(DrFrame):
    helpstring = \
"\
There is the abbreviations.txt as sample file in the package.\n\n\
This Abbreviations plugin should be compatible with the Scite Abbreviations.\n\
newlines should be replaced literally with '\\n': \n\
example: i=if (|) {\\n\\n}\n\n\
'#' at beginning is a comment line (if you need '#' as first char, use '##' instead).\n\
example: ##i=#ifdef\n\n\
'|' in the expanded section shows, where the cursor should be positioned (use '||' for inserting the '|' char. in the expansion text secion).\n\
example:me=messagebox(|first par || second par)\n\n\
the first '=' is the delimiter between abbreviation and expansion (if you have a '=' in abbreviation, use '==').\n\
example: a==b=alptha=beta a=b<expansion key> => alpha=beta\n\
"
    #DrFrame.ShowMessage(helpstring, "Help Abbreviations")
    drScrolledMessageDialog.ShowMessage (DrFrame, helpstring, "Help Abbreviations", size = (400, 340))

def OnPreferences(DrFrame):

    def GetIntFromBool (b):
        if b:
            return 1
        else:
            return 0

    d = wx.Dialog(DrFrame, -1, "Abbreviatons Preferences", wx.DefaultPosition, wx.Size(192, 120), wx.DEFAULT_DIALOG_STYLE | wx.THICK_FRAME)
    chkSciteCompatible = wx.CheckBox(d, -1, "Scite Compatible", wx.Point(20, 20))
    wx.Button(d, wx.ID_CANCEL, "Cancel", wx.Point(10, 50))
    wx.Button(d, wx.ID_OK, "Ok", wx.Point(90, 50))
    chkSciteCompatible.SetValue(DrFrame.Abbreviation_SciteCompatible)

    if d.ShowModal() == wx.ID_OK:
        f = file(DrFrame.pluginspreferencesdirectory + "/abbreviations.preferences.dat", 'w')
        DrFrame.Abbreviation_SciteCompatible = chkSciteCompatible.GetValue()
        f.write("<abbreviations.scitecompatible>" + str(GetIntFromBool (DrFrame.Abbreviation_SciteCompatible)) + "</abbreviations.scitecompatible>\n")
        f.close()
    d.Destroy()

class ShowAbbreviationsDlg(wx.Dialog):
    def __init__ (self, parent, choices, suggest, reduce):
        wx.Dialog.__init__ (self, parent, -1, "Show Abbreviations", wx.DefaultPosition,  wx.Size(600, 400), wx.DEFAULT_DIALOG_STYLE | wx.MAXIMIZE_BOX | wx.THICK_FRAME | wx.RESIZE_BORDER)
        self.btnOk = wx.Button(self, wx.ID_OK, "&Ok", pos = (500, 50))
        self.btnCancel = wx.Button(self, wx.ID_CANCEL, "&Cancel", pos = (500, 100))
        self.list = wx.ListCtrl(self, 403, (20, 35), (460, 315), wx.LC_REPORT|wx.SUNKEN_BORDER )#, choices = choices)
        self.list.InsertColumn(0, "Abbreviation",  wx.LIST_FORMAT_LEFT, 120)
        self.list.InsertColumn(1, "Expansion",  wx.LIST_FORMAT_LEFT, 336)

        index = 0
        for i in range (len(choices)):
            to_insert = True
            if reduce != 0:
                #print suggest, reduce
                if choices [i][0].lower().find (suggest.lower()) != 0:
                    to_insert = False
            if to_insert:
                self.list.InsertStringItem(index, choices [i][0])
                self.list.SetStringItem(index, 1, choices [i][1])
                self.list.SetItemData(index, index)
                index += 1

        self.Bind(wx.EVT_BUTTON,  self.OnbtnOk, id = wx.ID_OK)
        self.Bind(wx.EVT_BUTTON,  self.OnbtnCancel, id = wx.ID_CANCEL)
        self.Bind(wx.EVT_LISTBOX_DCLICK, self.OnbtnOk)
        #try to jump to next possible completition
        if suggest and reduce == 0:
            for i in range (len(choices)):
                if choices [i][0].lower() >= suggest.lower():
                    if i > 0:
                        i -= 1
                    self.list.SetItemState(i, wx.LIST_STATE_SELECTED|wx.LIST_STATE_FOCUSED , wx.LIST_STATE_SELECTED|wx.LIST_STATE_FOCUSED)
                    break
            else:
                self.list.SetItemState(len(choices) - 1, wx.LIST_STATE_SELECTED|wx.LIST_STATE_FOCUSED , wx.LIST_STATE_SELECTED|wx.LIST_STATE_FOCUSED)
        else:
            self.list.SetItemState(0, wx.LIST_STATE_SELECTED|wx.LIST_STATE_FOCUSED , wx.LIST_STATE_SELECTED|wx.LIST_STATE_FOCUSED)

        self.list.SetFocus ()
        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnbtnOk, id = 403)

        self.btnOk.SetDefault()

    def OnbtnOk (self, event):
        self.EndModal(1)

    def OnbtnCancel (self, event):
        self.EndModal(0)

def Plugin(DrFrame):


    def AddAbbreviation (text):
        #replace '\n' with eol character
        text = text.replace ('\\n', DrFrame.txtDocument.GetEndOfLineCharacter())
        #reg_target_cursor_pos = re.compile("[^|]\|[^|]") #cursorpos
        #better solution
        reg_target_cursor_pos = re.compile("(?<!\|)\|(?!\|)")
        res = reg_target_cursor_pos.search(text)

        doubletest = reg_target_cursor_pos.findall(text)
        if len (doubletest) > 1:
            wx.MessageBox("Double Cursor Pos: Please check abbreviations file!", "Abbreviations", wx.ICON_INFORMATION)
        delimiter_pos =  -1
        if res:
            delimiter_pos = res.start()
            correct = text.count ('||', 0, delimiter_pos)
            delimiter_pos -= correct
            #print delimiter_pos

        text = text.replace ('||', '|')
        if delimiter_pos != -1:
            oldpos = DrFrame.txtDocument.GetCurrentPos()
            #remove '|' char
            text = text[:delimiter_pos] + text[delimiter_pos + 1:]
        DrFrame.txtDocument.AddText(text)
        #set cursor to destined position?
        if delimiter_pos != -1:
            DrFrame.txtDocument.SetSelection(oldpos + delimiter_pos, oldpos + delimiter_pos)

    def OnInsertAbbreviation (event):
        ReloadAbbreviationsFile()
        DrFrame.txtDocument.SetWordChars (DrFrame.Abbreviations_new_word_chars)
        pos = DrFrame.txtDocument.GetCurrentPos()
        beg = DrFrame.txtDocument.WordStartPosition(pos, 1)
        end = DrFrame.txtDocument.WordEndPosition(pos, 1)
        DrFrame.txtDocument.SetWordChars (DrFrame.Abbreviations_old_word_chars)

        if beg == end:
            OnShowAbbreviations(event)
        else:
            to_complete = DrFrame.txtDocument.GetTextRange(beg, end)
            if DrFrame.Abbreviation_SciteCompatible:
                for i in range (1, end - beg + 1):
                    to_complete = DrFrame.txtDocument.GetTextRange(end - i, end)
                    for abb in DrFrame.Abbreviations_Abbreviations:
                        if abb[0].lower().find (to_complete.lower()) == 0:
                            #clear entered word before
                            DrFrame.txtDocument.SetTargetStart (end - len(to_complete))
                            DrFrame.txtDocument.SetTargetEnd (end)
                            DrFrame.txtDocument.ReplaceTarget("")
                            #add abbreviation
                            AddAbbreviation (abb[1])
                            return
            else:
                #check multiple possible completitions
                pos_completition = 0
                for abb in DrFrame.Abbreviations_Abbreviations:
                    if abb[0].lower().find (to_complete.lower()) == 0:
                        pos_completition += 1
                #print pos_completition
                if pos_completition == 1:
                    for abb in DrFrame.Abbreviations_Abbreviations:
                        if abb[0].lower().find (to_complete.lower()) == 0:
                            #clear entered word before
                            DrFrame.txtDocument.SetTargetStart (beg)
                            DrFrame.txtDocument.SetTargetEnd (end)
                            DrFrame.txtDocument.ReplaceTarget("")
                            #add abbreviation
                            AddAbbreviation (abb[1])
                            return
                else:
                    OnShowAbbreviations(event, to_complete, pos_completition)

    def OnShowAbbreviations (event, suggest = None, reduce = 0):
        ReloadAbbreviationsFile()
        if DrFrame.Abbreviations_Abbreviations:
            dlg = ShowAbbreviationsDlg (DrFrame, DrFrame.Abbreviations_Abbreviations, suggest, reduce)
        else:
            wx.MessageBox("No Abbreviations!", "Show Abbreviations", wx.ICON_INFORMATION)
            return

        if dlg.ShowModal() == 0:
            return
        nItem = dlg.list.GetNextItem(-1, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED)
        item = dlg.list.GetItem(nItem, 1)
        text = item.GetText()

        #text = DrFrame.Abbreviations_Abbreviations[nItem][1]

        #scite pref: no clear
        if not DrFrame.Abbreviation_SciteCompatible:
            DrFrame.txtDocument.SetWordChars (DrFrame.Abbreviations_new_word_chars)
            pos = DrFrame.txtDocument.GetCurrentPos()
            beg = DrFrame.txtDocument.WordStartPosition(pos, 1)
            end = DrFrame.txtDocument.WordEndPosition(pos, 1)
            DrFrame.txtDocument.SetWordChars (DrFrame.Abbreviations_old_word_chars)
            DrFrame.txtDocument.SetTargetStart (beg)
            DrFrame.txtDocument.SetTargetEnd (end)
            DrFrame.txtDocument.ReplaceTarget("")
        AddAbbreviation (text)

    def OnEditAbbreviations (event):
        alreadyopen = DrFrame.GetAlreadyOpen()
        #alreadyopen = map(lambda x: x.filename, DrFrame.txtDocumentArray)
        f = DrFrame.Abbreviations_AbbreviationsFile
        if f in alreadyopen:
            i = alreadyopen.index(f)
            DrFrame.setDocumentTo(i)
        else:
            DrFrame.OpenFile(f, True, False)

    def CompareLowerCase(n1, n2):
        if n1[0].lower() == n2[0].lower():
            return 0
        elif n1[0].lower() < n2[0].lower():
            return -1
        return 1

    def ReloadAbbreviationsFile():
        if DrFrame.Abbreviations_mtime != int(os.stat(DrFrame.Abbreviations_AbbreviationsFile).st_mtime):
            if DrFrame.Abbreviations_mtime != -1:
                wx.MessageBox("Reload Abbreviations file!", "Abbreviations", wx.ICON_INFORMATION)
            DrFrame.Abbreviations_mtime = int(os.stat(DrFrame.Abbreviations_AbbreviationsFile).st_mtime)
            f = file (DrFrame.Abbreviations_AbbreviationsFile, "r")
            text = f.read()
            f.close()
            #edited windows eol?
            text = text.replace ('\r\n', '\n')
            #edited mac eol?
            text = text.replace ('\r', '\n')
            entries = text.split ('\n')
            DrFrame.Abbreviations_Abbreviations = []
            for e in entries:
                #e = e.strip()
                if e.find ('#') == 0:
                    if e.find ('##') != 0:
                        #comment line: ignore this
                        continue
                    else:
                        e = e [1:]
                equal_sign = re.compile ('[^=]=[^=]')
                equal_sign_res = equal_sign.search (e)
                delimiter = -1
                if not equal_sign_res:
                    if e:
                        wx.MessageBox("Error: No Delimiter in (%s): Please check abbreviations file!" % e, "Abbreviations", wx.ICON_INFORMATION)
                    continue
                else:
                    delimiter = equal_sign_res.start()+1

                if delimiter == -1:
                    #no abbreviation line
                    continue
                if delimiter == 0:
                    #looks buggy entry ignore
                    continue
                abb = e[:delimiter]
                abb = abb.replace ('==', '=')
                exp = e[delimiter + 1:]
                #exp = exp.replace ('==', '=')
                DrFrame.Abbreviations_Abbreviations.append ([abb, exp])

            #check for duplicated entries
            to_correct = False
            g={}
            for u in DrFrame.Abbreviations_Abbreviations:
                if g.has_key(u[0]):
                    to_correct = True
                    wx.MessageBox("Duplicate Entry: (%s: %s)!" % (u[0], u[1]), "Abbreviations", wx.ICON_INFORMATION)
                g[u[0]]=u[1]
            if to_correct:
                DrFrame.Abbreviations_Abbreviations = []
                for z in g:
                    DrFrame.Abbreviations_Abbreviations.append ([z, g[z]])

            #lowercase sort
            DrFrame.Abbreviations_Abbreviations.sort(CompareLowerCase)

    DrFrame.Abbreviations_AbbreviationsDir = DrFrame.pluginsuserdatadirectory + "/abbreviations"
    DrFrame.Abbreviations_AbbreviationsFile = DrFrame.Abbreviations_AbbreviationsDir + "/abbreviations.txt"
    DrFrame.Abbreviations_Abbreviations = []
    DrFrame.Abbreviation_SciteCompatible = True
    prefsfile = DrFrame.pluginspreferencesdirectory + "/abbreviations.preferences.dat"
    if os.path.exists(prefsfile):
        f = file(prefsfile, 'r')
        text = f.read()
        f.close()
        DrFrame.Abbreviation_SciteCompatible = drPrefsFile.GetPrefFromText(DrFrame.Abbreviation_SciteCompatible, text, "abbreviations.scitecompatible", True)

    #create path?
    if not os.path.exists(DrFrame.Abbreviations_AbbreviationsDir):
        os.mkdir(DrFrame.Abbreviations_AbbreviationsDir)

    DrFrame.Abbreviations_old_word_chars = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    DrFrame.Abbreviations_new_word_chars = DrFrame.Abbreviations_old_word_chars + "#|("
    DrFrame.Abbreviations_mtime = -1


    #create file?
    if not os.path.exists(DrFrame.Abbreviations_AbbreviationsFile):
        file(DrFrame.Abbreviations_AbbreviationsFile, "w").close()
        DrFrame.Abbreviations_mtime = int(os.stat(DrFrame.Abbreviations_AbbreviationsFile).st_mtime)
    else:
        ReloadAbbreviationsFile()

    ID_INSERTABBREVIATION = DrFrame.GetNewId()
    ID_SHOWABBREVIATIONS = DrFrame.GetNewId()
    ID_EDITABBREVIATIONS = DrFrame.GetNewId()


    DrFrame.Bind(wx.EVT_MENU, OnInsertAbbreviation, id = ID_INSERTABBREVIATION)
    DrFrame.Bind(wx.EVT_MENU, OnShowAbbreviations, id = ID_SHOWABBREVIATIONS)
    DrFrame.Bind(wx.EVT_MENU, OnEditAbbreviations, id = ID_EDITABBREVIATIONS)

    DrFrame.AddPluginShortcutFunction("Abbreviations", "Insert Abbreviation", OnInsertAbbreviation)
    DrFrame.AddPluginShortcutFunction("Abbreviations", "Show Abbreviations", OnShowAbbreviations)
    DrFrame.AddPluginShortcutFunction("Abbreviations", "Edit Abbreviations", OnEditAbbreviations)

    DrFrame.AddPluginPopUpMenuFunction("Abbreviations", "Insert Abbreviation", OnInsertAbbreviation)
    DrFrame.AddPluginPopUpMenuFunction("Abbreviations", "Show Abbreviations", OnShowAbbreviations)
    DrFrame.AddPluginPopUpMenuFunction("Abbreviations", "Edit Abbreviations", OnEditAbbreviations)

    DrFrame.LoadPluginShortcuts('Abbreviations')

    abbreviationsmenu = wx.Menu()
    abbreviationsmenu.Append(ID_INSERTABBREVIATION, DrFrame.GetPluginMenuLabel('Abbreviations', 'Insert Abbreviation', 'Insert Abbreviation'))
    abbreviationsmenu.Append(ID_SHOWABBREVIATIONS, DrFrame.GetPluginMenuLabel('Abbreviations', 'Show Abbreviations', 'Show Abbreviations'))
    abbreviationsmenu.Append(ID_EDITABBREVIATIONS, DrFrame.GetPluginMenuLabel('Abbreviations', 'Edit Abbreviations', 'Edit Abbreviations'))
    DrFrame.editmenu.AppendSeparator()
    DrFrame.editmenu.AppendMenu(DrFrame.GetNewId(), "Abbreviations", abbreviationsmenu)
