from Tkinter import *

import SearchEngine

from SearchDialogBase import SearchDialogBase



def replace(text):

    root = text._root()

    engine = SearchEngine.get(root)

    if not hasattr(engine, "_replacedialog"):

        engine._replacedialog = ReplaceDialog(root, engine)

    dialog = engine._replacedialog

    dialog.open(text)



class ReplaceDialog(SearchDialogBase):



    title = "Replace Dialog"

    icon = "Replace"



    def __init__(self, root, engine):

        SearchDialogBase.__init__(self, root, engine)

        self.replvar = StringVar(root)



    def open(self, text):

        SearchDialogBase.open(self, text)

        try:

            first = text.index("sel.first")

        except TclError:

            first = None

        try:

            last = text.index("sel.last")

        except TclError:

            last = None

        first = first or text.index("insert")

        last = last or first

        self.show_hit(first, last)

        self.ok = 1



    def create_entries(self):

        SearchDialogBase.create_entries(self)

        self.replent = self.make_entry("Replace with:", self.replvar)



    def create_command_buttons(self):

        SearchDialogBase.create_command_buttons(self)

        self.make_button("Find", self.find_it)

        self.make_button("Replace", self.replace_it)

        self.make_button("Replace+Find", self.default_command, 1)

        self.make_button("Replace All", self.replace_all)



    def find_it(self, event=None):

        self.do_find(0)



    def replace_it(self, event=None):

        if self.do_find(self.ok):

            self.do_replace()



    def default_command(self, event=None):

        if self.do_find(self.ok):

            self.do_replace()

            self.do_find(0)



    def replace_all(self, event=None):

        prog = self.engine.getprog()

        if not prog:

            return

        repl = self.replvar.get()

        text = self.text

        res = self.engine.search_text(text, prog)

        if not res:

            text.bell()

            return

        text.tag_remove("sel", "1.0", "end")

        text.tag_remove("hit", "1.0", "end")

        line = res[0]

        col = res[1].start()

        if self.engine.iswrap():

            line = 1

            col = 0

        ok = 1

        first = last = None

        # XXX ought to replace circular instead of top-to-bottom when wrapping

        text.undo_block_start()

        while 1:

            res = self.engine.search_forward(text, prog, line, col, 0, ok)

            if not res:

                break

            line, m = res

            chars = text.get("%d.0" % line, "%d.0" % (line+1))

            orig = m.group()

            new = m.expand(repl)

            i, j = m.span()

            first = "%d.%d" % (line, i)

            last = "%d.%d" % (line, j)

            if new == orig:

                text.mark_set("insert", last)

            else:

                text.mark_set("insert", first)

                if first != last:

                    text.delete(first, last)

                if new:

                    text.insert(first, new)

            col = i + len(new)

            ok = 0

        text.undo_block_stop()

        if first and last:

            self.show_hit(first, last)

        self.close()



    def do_find(self, ok=0):

        if not self.engine.getprog():

            return False

        text = self.text

        res = self.engine.search_text(text, None, ok)

        if not res:

            text.bell()

            return False

        line, m = res

        i, j = m.span()

        first = "%d.%d" % (line, i)

        last = "%d.%d" % (line, j)

        self.show_hit(first, last)

        self.ok = 1

        return True



    def do_replace(self):

        prog = self.engine.getprog()

        if not prog:

            return False

        text = self.text

        try:

            first = pos = text.index("sel.first")

            last = text.index("sel.last")

        except TclError:

            pos = None

        if not pos:

            first = last = pos = text.index("insert")

        line, col = SearchEngine.get_line_col(pos)

        chars = text.get("%d.0" % line, "%d.0" % (line+1))

        m = prog.match(chars, col)

        if not prog:

            return False

        new = m.expand(self.replvar.get())

        text.mark_set("insert", first)

        text.undo_block_start()

        if m.group():

            text.delete(first, last)

        if new:

            text.insert(first, new)

        text.undo_block_stop()

        self.show_hit(first, text.index("insert"))

        self.ok = 0

        return True



    def show_hit(self, first, last):

        text = self.text

        text.mark_set("insert", first)

        text.tag_remove("sel", "1.0", "end")

        text.tag_add("sel", first, last)

        text.tag_remove("hit", "1.0", "end")

        if first == last:

            text.tag_add("hit", first)

        else:

            text.tag_add("hit", first, last)

        text.see("insert")

        text.update_idletasks()



    def close(self, event=None):

        SearchDialogBase.close(self, event)

        self.text.tag_remove("hit", "1.0", "end")

