import color, mode, tab
from lex import Grammar, PatternRule, RegionRule
from mode.python import StringGrammar
from mode.c import CTabber

class JavaGrammar(Grammar):
    rules = [
        PatternRule(r'import', r'(?<=import ) *[a-zA-Z0-9_.*]+'),
        PatternRule(r'package', r'(?<=package ) *[a-zA-Z0-9_.*]+'),

        RegionRule(r'doccomment', '/\*\*', Grammar, '\*/'),
        RegionRule(r'comment', '/\*', Grammar, '\*/'),
        PatternRule(r'comment', r'//.*$'),

        PatternRule(r'keyword', r"(?:abstract|assert|boolean|break|byte|case|catch|char|class|continue|default|double|do|else|extends|finally|final|float|for|if|implements|import|instanceof|interface|int|long|native|new|package|private|protected|public|return|short|static|switch|super|synchronized|threadsafe|throws|throw|transient|try|void|while)(?![a-zA-Z_])"),
        PatternRule(r'label', r'[a-zA-Z_][a-zA-Z0-9_]*(?=:)'),

        PatternRule(r'builtin', r"(?:null|true|false|this)"),
        PatternRule(r'identifier', r"[a-zA-Z_][a-zA-Z0-9_]*"),
        PatternRule(r"unop", r"\+=|-=|\*=|/=|//=|%=|&=\|\^=|>>=|<<=|\*\*="),
        PatternRule(r'binop', r"\+|<>|<<|<=|<|-|>>|>=|>|\*\*|&|\*|\||/|\^|==|//|~|!=|%"),

        PatternRule(r"delimiter", r"->|\.|\(|\)|\[|\]|{|}|@|,|:|`|;|=|\?"),

        PatternRule(r"integer", r"(?:0(?![x0-9])|[1-9][0-9]*|0[0-7]+|0[xX][0-9a-fA-F]+)[lL]?"),
        PatternRule(r"float", r"[0-9]+\.[0-9]*|\.[0-9]+|(?:[0-9]|[0-9]+\.[0-9]*|\.[0-9]+)[eE][\+-]?[0-9]+"),
        RegionRule(r'string', '"', StringGrammar, '"'),
        PatternRule(r'char', r"'.'|'\\.'|'\\[0-7]{3}'"),
        PatternRule(r"eol", r"\n$"),
    ]

class JavaTabber(CTabber):
    def is_base(self, y):
        if y == 0:
            return True
        #return False

        highlighter = self.mode.window.buffer.highlights[self.mode.name()]
        if not highlighter.tokens[y]:
            return False

        for t in highlighter.tokens[y]:
            if t.name == 'null':
                pass
            elif t.name == 'keyword':
                if t.string in ('class', 'interface'):
                    return True
                elif t.string in ('public', 'private', 'protected', 'static',
                                  'final', 'native', 'synchronized', 'abstract',
                                  'threadsafe', 'transient'):
                    pass
                else:
                    return False
            else:
                return False
                
        return False

        # detecting function declarations is annoying; this assumes that people
        # won't put a variable type and name on different lines, but that they
        # might do that for function return type and name.
        #
        # unfortunately, valid function return types might include any of the
        # four types of tokens below
        decl = False
        for t in highlighter.tokens[y]:
            if t.name in ('keyword', 'identifier', 'structname', 'enumname'):
                decl = True
                continue
            if decl and t.name == 'function':
                break
            else:
                decl = False
                break
        if decl:
            return True

        return False
        
    def _handle_open_token(self, currlvl, y, i):
        self._opt_pop('cont')
        token = self.get_token(y, i)
        if token.string == '{':
            self._opt_pop('cond')
        currlvl = tab.StackTabber._handle_open_token(self, currlvl, y, i)
        return currlvl
    def _handle_close_token(self, currlvl, y, i):
        self._opt_pop('cont')
        currlvl = tab.StackTabber._handle_close_token(self, currlvl, y, i)
        token = self.get_token(y, i)
        if self.is_rightmost_token(y, i):
            if token.string == '}':
                self._opt_pop('cond')
                self._opt_pop('cont')
            elif self._peek_name() == 'cond':
                pass
            else:
                self._opt_append('cont', currlvl + 4)
        return currlvl
    def _handle_other_token(self, currlvl, y, i):
        token  = self.get_token(y, i)
        fqname = token.fqname()
        if fqname == 'delimiter' and token.string == ';':
            self._opt_pop('cond')
            self._opt_pop('cont')
            self._opt_pop('cond')

        elif fqname == 'keyword':
            if token.string in ('do', 'else', 'for', 'if', 'while'):
                self._append('cond', currlvl + 4)
            elif token.string == 'break':
                self._opt_pop('case', 'while', 'for')
            elif token.string == 'continue':
                self._opt_pop('while', 'for')
            elif token.string == 'case':
                self._opt_pop('case')
                currlvl = self.get_curr_level()
                self._opt_append('case', currlvl + 4)

        elif fqname == 'string.start':
            self._opt_append('string', None)
        elif fqname == 'string.end':
            self._opt_pop('string')
            if self.is_rightmost_token(y, i):
                self._opt_append('cont', currlvl + 4)

        # TODO: this could be a lot better
        elif fqname == 'macro':
            currlvl = 0
        elif fqname.startswith('macro.start'):
            self._opt_append('macro', None)
            currlvl = 0
        elif fqname.startswith('macro.end'):
            self._opt_pop('macro', None)                               

        elif fqname.startswith('macroblock.start'):
            self._opt_append('macroblock', None)                               
            currlvl = 0
        elif fqname.startswith('macroblock.end'):
            self._opt_pop('macroblock', None)                               

        if self.is_rightmost_token(y, i):
            if self._has_markers() and self._peek_name() == 'cond':
                pass
            elif(not fqname.startswith('string') and
                 not fqname.startswith('comment') and
                 not fqname.startswith('macro') and
                 not fqname == 'delimiter' and
                 not fqname == 'header' and
                 not fqname == 'null' and
                 not fqname == 'eol' and
                 token.string not in ('}', ';', '(', '{', '[', ',')):
                self._opt_append('cont', currlvl + 4)
        return currlvl

class Java(mode.Fundamental):
    modename    = 'Java'
    extensions  = ['.java']
    tabbercls   = JavaTabber
    grammar     = JavaGrammar
    opentokens  = ('delimiter',)
    opentags    = {'(': ')', '[': ']', '{': '}'}
    closetokens = ('delimiter',)
    closetags   = {')': '(', ']': '[', '}': '{'}
    colors      = {
        'comment':            ('red', 'default'),
        'comment.start':      ('red', 'default'),
        'comment.end':        ('red', 'default'),
        'comment.null':       ('red', 'default'),

        'doccomment.start':      ('red', 'default'),
        'doccomment.end':        ('red', 'default'),
        'doccomment.null':       ('red', 'default'),

        'import':  ('blue', 'default'),
        'label':    ('magenta', 'default'),
        'keyword':  ('cyan', 'default'),
        'function': ('blue', 'default'),
        'builtin':  ('magenta', 'default'),

        'char':           ('green', 'default'),
        'string.start':   ('green', 'default'),
        'string.octal':   ('green', 'default'),
        'string.escaped': ('green', 'default'),
        'string.null':    ('green', 'default'),
        'string.end':     ('green', 'default'),
        'integer':        ('green', 'default'),
        'float':          ('green', 'default'),
    }
    def __init__(self, w):
        mode.Fundamental.__init__(self, w)
        self.add_bindings('close-paren', (')',))
        self.add_bindings('close-brace', ('}',))
        self.add_bindings('close-bracket', (']',))

install = Java.install