import regex, util
from point2 import Point

class Marker:
    def __init__(self, name, level):
        self.name  = name
        self.level = level
    def __repr__(self):
        return '<Marker(%r, %r)>' % (self.name, self.level)

class Tabber:
    wsre = regex.whitespace
    wst  = ('null', 'eol',)
    sre  = regex.space
    st   = ('null',)
    def __init__(self, m):
        self.mode  = m
        self.lines = {}

    def get_highlighter(self):
        return self.mode.window.buffer.highlights[self.mode.name()]
    def get_tokens(self, y):
        return self.mode.window.buffer.highlights[self.mode.name()].tokens[y]
    def get_token(self, y, i):
        return self.mode.window.buffer.highlights[self.mode.name()].tokens[y][i]

    def token_is_whitespace(self, y, i):
        token = self.get_token(y, i)
        return token.name in self.wst and self.wsre.match(token.string)
    def token_is_space(self, y, i):
        token = self.get_token(y, i)
        return token.name in self.st and self.sre.match(token.string)

    def get_next_left_token(self, y, i):
        tokens = self.get_tokens(y)
        assert i >= 0 and i < len(tokens)
        for j in range(1, i):
            if not self.token_is_whitespace(y, i - j):
                return tokens[i - j]
        return None
    def get_next_right_token(self, y, i):
        tokens = self.get_tokens(y)
        assert i >= 0 and i < len(tokens)
        for j in range(i + 1, len(tokens)):
            if not self.token_is_whitespace(y, j):
                return tokens[j]
        return None
    def is_leftmost_token(self, y, i):
        return self.get_next_left_token(y, i) is None
    def is_rightmost_token(self, y, i):
        return self.get_next_right_token(y, i) is None
    def is_only_token(self, y, i):
        return self.is_leftmost_token(y, i) and self.is_rightmost_token(y, i)

    def get_leftmost_token(self, y):
        tokens = self.get_tokens(y)
        for i in range(0, len(tokens)):
            if not self.token_is_whitespace(y, i):
                return tokens[i]
        return None
    def get_rightmost_token(self, y):
        tokens = self.get_tokens(y)
        i = len(tokens) - 1
        for j in range(0, len(tokens)):
            if not self.token_is_whitespace(y, i - j):
                return tokens[i - j]
        return None

    def region_added(self, p, newlines):
        self.lines = {}
    def region_removed(self, p1, p2):
        self.lines = {}

    def is_base(self, y):
        return True
    def get_level(self, y):
        if y in self.lines:
            return self.lines[y]
        else:
            self._calc_level(y)
            return self.lines.get(y)
    def _calc_level(self, y):
        pass

class StackTabber(Tabber):
    def __init__(self, m):
        self.mode    = m
        self.lines   = {}
        self.record  = {}
        self.markers = []

    def get_curr_level(self):
        if self.markers:
            return self.markers[-1].level
        else:
            return 0

    def region_added(self, p, newlines):
        self.lines   = {}
        self.record  = {}
        self.markers = []
    def region_removed(self, p1, p2):
        self.lines   = {}
        self.record  = {}
        self.markers = []

    def is_base(self, y):
        return y == 0
    def _calc_level(self, y):
        # first we need to step back to find the last place where we have tab
        # stops figured out, or a suitable place to start
        target = y
        while not self.is_base(y) and y > 0:
            y -= 1

        # ok now, let's do this shit
        self.markers = []
        currlvl      = 0
        while y <= target:
            currlvl = self.get_curr_level()
            tokens = self.get_tokens(y)
            for i in range(0, len(tokens)):
                currlvl = self._handle_token(currlvl, y, i)
            self.lines[y]  = currlvl
            self.record[y] = tuple(self.markers)
            y += 1

    def _handle_token(self, currlvl, y, i):
        token = self.get_token(y, i)
        s = token.string

        if token.name in self.mode.closetokens and s in self.mode.closetags:
            currlvl = self._handle_close_token(currlvl, y, i)
        elif token.name in self.mode.opentokens and s in self.mode.opentags:
            currlvl = self._handle_open_token(currlvl, y, i)
        else:
            currlvl = self._handle_other_token(currlvl, y, i)
        return currlvl

    def _handle_open_token(self, currlvl, y, i):
        token  = self.get_token(y, i)
        rtoken = self.get_next_right_token(y, i)
        if rtoken is None:
            level = self.get_curr_level() + 4
        else:
            level = rtoken.x
        self._append(token.string, level)
        return currlvl
    def _handle_close_token(self, currlvl, y, i):
        token = self.get_token(y, i)
        s1 = token.string
        if not self.markers:
            raise Exception, "unmatched closing token %r" % s1
        s2 = self.markers[-1].name
        if self.mode.closetags[s1] == s2:
            self._pop()
            if self.is_leftmost_token(y, i):
                currlvl = self.get_curr_level()
            #else:
            #    raise Exception, "hmmmm: %r" % self.get_next_left_token(y, i)
        else:
            raise Exception, "mismatched closing tag %r vs %r" % (s2, s1)
        return currlvl
    def _handle_other_token(self, currlvl, y, i):
        return currlvl

    def _has_markers(self):
        return len(self.markers) > 0
    def _empty(self):
        return len(self.markers) == 0
    def _append(self, name, level):
        self.markers.append(Marker(name, level))
    def _peek(self):
        return self.markers[-1]
    def _peek_name(self):
        return self.markers[-1].name
    def _peek_level(self):
        return self.markers[-1].level
    def _pop(self):
        self.markers.pop(-1)
    def _pop_until(self, *names):
        while self.markers:
            if self.markers[-1].name in names:
                self.markers.pop(-1)
                return
            else:
                self.markers.pop(-1)

    def _opt_append(self, name, level):
        if self.markers and self.markers[-1].name == name:
            pass
        else:
            self._append(name, level)
    def _opt_pop(self, *names):
        if self.markers and self.markers[-1].name in names:
            self.markers.pop(-1)