#!/usr/bin/env python3
# Copyright © 2012-13 Qtrac Ltd. All rights reserved.
# This program or module 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 3 of the
# License, or (at your option) any later version. It is provided for
# educational purposes and 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.

import os
import re
import tkinter as tk
import tkinter.ttk as ttk
if __name__ == "__main__": # For stand-alone testing with parallel TkUtil
    import sys
    sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
        "..")))
import TkUtil
from Globals import *


class Window(tk.Toplevel):

    def __init__(self, master, editor):
        super().__init__(master)
        self.withdraw()
        self.editor = editor
        self.editor.tag_config(FIND_TAG, background="yellow")
        self.editor.tag_config(REPLACE_TAG, background="#C1FFC1")
        self.create_ui()
        self.resizable(False, False)
        self.deiconify()
        if self.winfo_viewable():
            self.transient(master)
        self.wait_visibility()


    def create_ui(self):
        self.create_variables()
        self.create_widgets()
        self.create_layout()
        self.create_bindings()
        self.unextend()
        self.findEntry.focus()


    def create_variables(self):
        self.caseSensitive = tk.IntVar()
        self.caseSensitive.set(0)
        self.wholeWords = tk.IntVar()
        self.wholeWords.set(0)
        self.extensionWidgets = ()
        if not TkUtil.x11():
            self.images = {}
            imagePath = os.path.join(os.path.dirname(
                    os.path.realpath(__file__)), "images")
            for name in (EXTEND, UNEXTEND):
                filename = os.path.join(imagePath, name + "_16x16.gif")
                if os.path.exists(filename):
                    self.images[name] = tk.PhotoImage(file=filename)


    def create_widgets(self):
        self.findLabel = TkUtil.Label(self, text="Find:", underline=1)
        self.findEntry = ttk.Entry(self, width=25)
        self.replaceLabel = TkUtil.Label(self, text="Replace:",
                underline=1)
        self.replaceEntry = ttk.Entry(self, width=25)
        self.caseSensitiveCheckbutton = TkUtil.Checkbutton(self,
                text="Case Sensitive", underline=5,
                variable=self.caseSensitive)
        self.wholeWordsCheckbutton = TkUtil.Checkbutton(self,
                text="Whole Words", underline=0,
                variable=self.wholeWords)
        self.findButton = TkUtil.Button(self, text="Find", underline=0,
                command=self.find, default=tk.ACTIVE, state=tk.DISABLED)
        self.replaceButton = TkUtil.Button(self, text="Replace",
                underline=0, command=self.replace, state=tk.DISABLED)
        self.closeButton = TkUtil.Button(self, text="Close", underline=0,
                command=self.close)
        if TkUtil.x11():
            self.extendButton = TkUtil.ToggleButton(self, text="Extend",
                    underline=1, command=self.toggle_extend)
        else:
            self.extendButton = ttk.Button(self, text="Extend",
                    underline=1, command=self.toggle_extend,
                    image=self.images[UNEXTEND], compound=tk.LEFT)
        self.extensionWidgets = (self.replaceLabel, self.replaceEntry,
                self.replaceButton)


    def create_layout(self):
        pad = dict(padx=PAD, pady=PAD)
        padWE = dict(sticky=(tk.W, tk.E), **pad)
        self.findLabel.grid(row=0, column=0, sticky=tk.W, **pad)
        self.findEntry.grid(row=0, column=1, **padWE)
        self.replaceLabel.grid(row=1, column=0, sticky=tk.W, **pad)
        self.replaceEntry.grid(row=1, column=1, **padWE)
        self.caseSensitiveCheckbutton.grid(row=2, column=0, columnspan=2,
                **padWE)
        self.wholeWordsCheckbutton.grid(row=3, column=0, columnspan=2,
                **padWE)
        self.findButton.grid(row=0, column=2, **padWE)
        self.replaceButton.grid(row=1, column=2, **padWE)
        self.closeButton.grid(row=2, column=2, **padWE)
        self.extendButton.grid(row=3, column=2, **padWE)
        self.grid_columnconfigure(1, weight=1)
        self.minsize(180, 90)
        self.maxsize(600, 150)
        self.geometry("+{}+{}".format(self.master.winfo_rootx() + 200,
                self.master.winfo_rooty() - 30))


    def create_bindings(self):
        self.protocol("WM_DELETE_WINDOW", self.close)
        if not TkUtil.mac():
            self.bind("<Alt-c>", self.close)
            self.bind("<Alt-e>", lambda *args: self.replaceEntry.focus())
            self.bind("<Alt-f>", self.find)
            self.bind("<Alt-i>", lambda *args: self.findEntry.focus())
            self.bind("<Alt-r>", self.replace)
            self.bind("<Alt-s>",
                    lambda *args: self.caseSensitiveCheckbutton.invoke())
            self.bind("<Alt-w>",
                    lambda *args: self.wholeWordsCheckbutton.invoke())
            self.bind("<Alt-x>", lambda *args: self.extendButton.invoke())
        self.bind("<Return>", self.find)
        self.bind("<Escape>", self.close)
        self.findEntry.bind("<KeyRelease>", self.update_ui)
        self.replaceEntry.bind("<KeyRelease>", self.update_ui)


    def update_ui(self, event=None):
        state = "!" + tk.DISABLED if self.findEntry.get() else tk.DISABLED
        self.findButton.state((state,))
        self.replaceButton.state((state,))
        # We allow the replace button to be enabled even if there's no
        # replacement text since the user might want to replace
        # something with nothing.


    def find(self, event=None):
        text = self.findEntry.get()
        assert text
        length = len(text)
        caseInsensitive = not self.caseSensitive.get()
        wholeWords = self.wholeWords.get()
        if wholeWords:
            text = r"\m{}\M".format(re.escape(text)) # Tcl regex syntax
        self.editor.tag_remove(FIND_TAG, "1.0", tk.END)
        insert = self.editor.index(tk.INSERT)
        start = self.editor.search(text, insert, nocase=caseInsensitive,
                regexp=wholeWords)
        if start and start == insert:
            start = self.editor.search(text, "{} +{} char".format(
                    insert, length), nocase=caseInsensitive,
                    regexp=wholeWords)
        if start:
            self.editor.mark_set(tk.INSERT, start)
            self.editor.see(start)
            end = "{} +{} char".format(start, length)
            self.editor.tag_add(FIND_TAG, start, end)
            return start, end
        return None, None


    def replace(self, event=None):
        start, end = self.find()
        if start is not None and end is not None:
            self.editor.tag_remove(FIND_TAG, start, end)
            self.editor.edit_separator()
            self.editor.delete(start, end)
            self.editor.insert(start, self.replaceEntry.get(), REPLACE_TAG)
            self.editor.edit_separator()
            # tkinter.Text for Tk 8.5.11 doesn't support replace()


    def close(self, event=None):
        self.editor.tag_remove(FIND_TAG, "1.0", tk.END)
        self.editor.tag_remove(REPLACE_TAG, "1.0", tk.END)
        self.withdraw()


    def toggle_extend(self, event=None):
        if self.extendButton.instate((TkUtil.NOT_SELECTED,)):
            self.extend()
        else:
            self.unextend()


    def extend(self):
        self.extendButton.state((TkUtil.SELECTED,))
        self.extendButton.config(text="Unextend",
                underline=3 if not TkUtil.mac() else -1)
        if not TkUtil.x11():
            self.extendButton.config(image=self.images[UNEXTEND])
        self.title("Find and Replace \u2014 {}".format(APPNAME))
        for widget in self.extensionWidgets:
            widget.grid()


    def unextend(self):
        self.extendButton.state((TkUtil.NOT_SELECTED,))
        self.extendButton.config(text="Extend",
                underline=1 if not TkUtil.mac() else -1)
        if not TkUtil.x11():
            self.extendButton.config(image=self.images[EXTEND])
        self.title("Find \u2014 {}".format(APPNAME))
        for widget in self.extensionWidgets:
            widget.grid_remove()


if __name__ == "__main__":
    if sys.stdout.isatty():
        application = tk.Tk()
        window = tk.Text(application)
        window.insert(tk.END, """This is some text and here is some
more. And this is yet more and more and MORE. And moreover there's
overmore to come some more.""")
        window.pack()
        find = Window(application, window)
        application.bind("<Control-q>", lambda *args: application.quit())
        window.bind("<Control-q>", lambda *args: application.quit())
        application.geometry("+0+200")
        application.mainloop()
    else:
        print("Loaded OK")