import re, sets, string

import color, method, minibuffer, mode, searchutil
from point import Point

selected_color   = 'magenta'
unselected_color = 'yellow'

def _make_regex(w, s):
    try:
        if w.buffer.method.is_literal:
            s = searchutil.escape_literal(s)
            if s.islower():
                return re.compile(s, re.IGNORECASE)
            else:
                return re.compile(s)
        else:
            return re.compile(s)
    except:
        raise searchutil.IllegalPatternError, "failed to compile: %r" % s

class SearchNext(method.Method):
    def execute(self, w, **vargs):
        w.buffer.method.direction = 'next'
        s = w.buffer.make_string()
        if s:
            try:
                r = _make_regex(w, s)
                searchutil.find_next(r, w.buffer.method.old_window, move=True)
            except searchutil.IllegalPatternError:
                w.application.clear_highlighted_ranges('search')
        else:
            action = InsertSearchString(w.application.last_search)
            action.execute(w)

class SearchPrevious(method.Method):
    def execute(self, w, **vargs):
        w.buffer.method.direction = 'previous'
        if not w.buffer.make_string():
            return
        else:
            s  = w.buffer.make_string()
            w2 = w.buffer.method.old_window
            try:
                r  = _make_regex(w, s)
                searchutil.find_previous(r, w2, move=True)
            except searchutil.IllegalPatternError:
                w.application.clear_highlighted_ranges('search')

class EndSearch(method.Method):
    def execute(self, w, **vargs):
        old_w = w.buffer.method.old_window
        old_c = w.buffer.method.old_cursor
        _end(w)
        old_w.set_mark_point(old_c)
        w.set_error("Mark set to search start")

class CancelSearch(method.Method):
    def execute(self, w, **vargs):
        w.buffer.method.old_window.goto(w.buffer.method.old_cursor)
        _end(w)
        w.set_error("Search cancelled")

class SearchDeleteLeft(method.Method):
    def execute(self, w, **vargs):
        w.left_delete()
        _post_delete(w)
class SearchDeleteLeftWord(method.Method):
    def execute(self, w, **vargs):
        w.kill_left_word()
        _post_delete(w)
def _post_delete(w):
    old_cursor = w.buffer.method.old_cursor
    old_w      = w.buffer.method.old_window
    old_w.goto(old_cursor)
    if not w.buffer.make_string():
        w.application.clear_highlighted_ranges('search')
        return
    s  = w.buffer.make_string()
    w2 = w.buffer.method.old_window
    try:
        r  = _make_regex(w, s)
        if w.buffer.method.direction == 'next':
            searchutil.find_next(r, w2, move=False)
        else:
            searchutil.find_previous(r, w2, move=False)
    except searchutil.IllegalPatternError:
        w.application.clear_highlighted_ranges('search')

class InsertSearchString(method.Method):
    def __init__(self, s):
        self.name = 'insert-search-string-%s' % (s)
        self.string = s
        self.args = []
        self.help = ''
    def execute(self, w, **vargs):
        w.insert_string_at_cursor(self.string)
        s = w.buffer.make_string()
        if not s:
            w.application.clear_highlighted_ranges('search')
            return
        else:
            try:
                r  = _make_regex(w, s)
                w2 = w.buffer.method.old_window
                if w.buffer.method.direction == 'next':
                    searchutil.find_next(r, w2, move=False)
                else:
                    searchutil.find_previous(r, w2, move=False)
            except searchutil.IllegalPatternError:
                w.application.clear_highlighted_ranges('search')

def _end(w):
    w.application.close_mini_buffer()
    w.application.clear_highlighted_ranges('search')
    w.application.last_search  = w.buffer.make_string()

class Search(mode.Fundamental):
    modename = 'Search'
    actions = [SearchNext, SearchPrevious, EndSearch, CancelSearch,
               SearchDeleteLeft, SearchDeleteLeftWord]
    def __init__(self, w):
        mode.Fundamental.__init__(self, w)

        # clear out all the defaults that we don't want/need, and add ours
        self.actions  = {}
        self.bindings = {}
        self.add_bindings('search-next', ('C-s',))
        self.add_bindings('search-previous', ('C-r',))
        self.add_bindings('end-search', ('RETURN', 'C-n', 'C-p', 'C-a', 'C-e', 'C-f', 'C-b',))
        self.add_bindings('cancel-search', ('C-]',))
        self.add_bindings('search-delete-left', ('DELETE', 'BACKSPACE',))
        self.add_bindings('search-delete-left-word', ('M-DELETE', 'M-BACKSPACE',))

        # create all the insert actions for the character ranges we like
        for collection in (string.letters, string.digits, string.punctuation):
            for c in collection:
                self.add_action_and_bindings(InsertSearchString(c), (c,))
        self.add_action_and_bindings(InsertSearchString(' '), ('SPACE',))

install = Search.install