import re
from method import Method
from mode import Fundamental
import minibuffer, searchutil

subgroup_re = re.compile(r'((?:\\\\)*)\\(0|[1-9][0-9]*)')

class ReplaceOne(Method):
    'In a replace command, replace the next occurance'
    def execute(self, w, **vargs):
        m = w.buffer.method
        _replace(m)
        _find_next(m, False)
        _finish(m, w)

class ReplaceDone(Method):
    'In a replace command, replace the next occurance and exit'
    def execute(self, w, **vargs):
        m = w.buffer.method
        _replace(m)
        _end(w)
        w.set_error("Replace done")

class SkipReplace(Method):
    'In a replace command, skip the next occurance'
    def execute(self, w, **vargs):
        m = w.buffer.method
        _find_next(m, True)
        _finish(m, w)

class ReplaceAll(Method):
    'In a replace command, replace all remaining occurances'
    # FIXME: this is super slow
    def execute(self, w, **vargs):
        m = w.buffer.method
        while m.p1 is not None:
            _replace(m)
            _find_next(m, False)
        _end(w)
        w.set_error("Replace ended")

class CancelReplace(Method):
    'Cancel a currently running replace command'
    def execute(self, w, **vargs):
        _end(w)
        w.set_error("Replace cancelled")

def _find_next(m, move=False):
    s = m.before
    w = m.old_window
    c = w.logical_cursor()
    try:
        if m.is_literal:
            r = re.compile(searchutil.escape_literal(s))
        else:
            r = re.compile(s)
    except:
        (m.p1, m.p2) = (None, None)
        return False
        
    if move:
        newc = searchutil.find_next(r, w, False, start=c.add(1, 0))
    else:
        newc = searchutil.find_next(r, w, False, start=c.add(0, 0))

    if newc:
        (m.p1, m.p2, m.match) = newc
        return True
    else:
        (m.p1, m.p2, m.match) = (None, None, None)
        return False

def _get_before(m):
    if m.match is None:
        return m.before
    else:
        return m.match.group(0)
    
def _get_after(m):
    if m.after is None:
        return None
    elif m.match is None:
        return m.after

    def _repl(match):
        (pre, num) = (match.group(1), int(match.group(2)))
        if num == 0 or m.match.lastindex and num <= m.match.lastindex:
            return pre + m.match.group(num)
        else:
            return match.group(0)

    return subgroup_re.sub(_repl, m.after)

def _set_prompt(m):
    w = m.old_window
    if m.p1 is None:
        #w.application.mini_prompt = '%r was not found' % m.before
        w.application.mini_prompt = '[%r] %r was not found' % (m.p1, m.before)
        return
    (x, y) = m.p1.xy()
    count = 0
    while y < len(w.buffer.lines):
        count += w.buffer.lines[y][x:].count(m.before)
        y += 1
        x = 0

    after  = _get_after(m)
    before = _get_before(m)

    if count > 1:
        p = 'Replace %r with %r [ynadq] (%d occurances)?' % (before, after, count)
    elif count == 1:
        p = 'Replace %r with %r [ynadq] (1 occurance)?' % (before, after)
    elif count == 0:
        p = 'Replace %r with %r [ynadq] (0 occurances)?' % (before, after)
        #raise Exception("this can't happen")
    else:
        raise Exception("this REALLY can't happen")
    w.application.mini_prompt = p

def _replace(m):
    m.old_window.buffer.delete(m.p1, m.p2)
    if m.after:
        after = _get_after(m)
        m.old_window.buffer.insert_string(m.p1, after)

def _finish(m, w):
    if m.p1 is None:
        _end(w)
        w.set_error("Replace ended")
    else:
        _set_prompt(m)

def _end(w):
    w.application.close_mini_buffer()
    w.application.clear_highlighted_ranges('search')
    w.buffer.method.old_cursor = None
    w.buffer.method.old_window = None
    assert not w.application.mini_active

class Replace(Fundamental):
    name    = 'Replace'
    actions = [ReplaceAll, ReplaceDone, ReplaceOne, SkipReplace, CancelReplace]
    def __init__(self, w):
        Fundamental.__init__(self, w)

        self.actions = {}
        self.bindings = {}
        self.add_bindings('replace-all', ('a', '!',))
        self.add_bindings('replace-done', ('d',))
        self.add_bindings('replace-one', ('y', 'SPACE',))
        self.add_bindings('skip-replace', ('n', 'DELETE',))
        self.add_bindings('cancel-replace', ('q', 'RETURN', 'C-]', 'C-n', 'C-p', 'C-a', 'C-e', 'C-f', 'C-b', 'C-g'))

        m = w.buffer.method
        found = _find_next(m, False)
        if not found:
            w.set_error('%r was not found' % m.before)
            raise minibuffer.MiniBufferError
        _set_prompt(m)

install = Replace.install