import mode, tab
import context
from lex import Grammar, PatternRule, RegionRule, PatternMatchRule
from mode.python import StringGrammar2
from mode.c import CTabber2
from parse import Any, And, Or, Optional, Name, Match, Matchs

class CommentGrammar(Grammar):
    rules = [
        PatternMatchRule(r'x', r'(@[a-z]+)( +)([^ ]+)', r'javadoc', r'spaces', r'javaname'),
        PatternRule(r"javadoc", r"@[a-z]+"),
        PatternRule(r"data", r"(?:[^@*]|\*(?!/))+"),
    ]

chr1 = '[a-zA-Z_]'
chr2 = '[a-zA-Z0-9_]'
word1 = chr1 + chr2 + '*'
word2 = '(?:' + word1 + r'\.' + ')*' + word1

class JavaGrammar(Grammar):
    rules = [
        PatternRule(r"spaces", r" +"),
        PatternMatchRule('x', r'(import)( +)(' + word2 + '(?:\.\*)?)', 'keyword', 'spaces', 'import'),
        PatternMatchRule('x', r'(package)( +)(' + word2 + ')', 'keyword', 'spaces', 'package'),
        PatternMatchRule('x', r'(class)( +)(' + word1 + ')', 'keyword', 'spaces', 'java_class'),
        PatternMatchRule('x', '(new)( +)(' + word2 + ')', 'keyword', 'spaces', 'java_class'),

        RegionRule(r'java_comment', '/\*', CommentGrammar, '\*/'),
        PatternRule(r'comment', r'//.*$'),

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

        PatternMatchRule('x', '(' + word2 + ')((?:\[\])?)( +)(' + word1 + ')( *)(\()',
                         'java_type', 'delimiter', 'spaces', 'java_function', 'spaces', 'delimiter'),
        PatternMatchRule('x', '(' + word2 + ')((?:\[\])?)( +)(' + word1 + ')(?! *\()',
                         'java_type', 'delimiter', 'spaces', 'identifier'),

        PatternRule(r'java_builtin', r"(?:null|true|false|this)"),
        PatternRule(r'identifier', word1),
        PatternRule(r"operator", r"\+=|-=|\*=|/=|//=|%=|&=\|\^=|>>=|<<=|\*\*="),
        PatternRule(r'operator', 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', '"', StringGrammar2, '"'),
        PatternRule(r'java_char', r"'.'|'\\.'|'\\[0-7]{3}'"),
        PatternRule(r"eol", r"\n$"),
    ]


CLASS_MATCH = And(Optional(Name('spaces')), 
                  Matchs('keyword', ('public', 'protected', 'private')),
                  Name('spaces'),
                  Match('keyword', 'class'),
                  Name('spaces'),
                  Name('java_class'))
CLASS_OFFSET = 1
METHOD_MATCH = And(Optional(Name('spaces')), 
                   Matchs('keyword', ('public', 'protected', 'private')),
                   Name('spaces'),
                   Optional(And(Match('keyword', 'static'), Name('spaces'))),
                   Any(),
                   Name('spaces'),
                   Name('java_function'),
                   Optional(Name('spaces')),
                   Match('delimiter', '('))
METHOD_OFFSET = 2

class JavaTabber2(tab.StackTabber2):
    open_tokens        = {'delimiter': {'{': '}', '(': ')', '[': ']'}}
    close_tokens       = {'delimiter': {'}': '{', ')': '(', ']': '['}}
    control_tokens     = {'keyword': {'if': 1, 'else': 1, 'while': 1, 'do': 1, 'for': 1}}
    end_at_eof         = False
    end_at_tokens      = {'delimiter': {';': 1}}
    nocontinue_tokens  = {'delimiter': {';': 1},
                          'java_comment.start': 1,
                          'java_comment.data': 1,
                          'java_comment.end': 1}
    start_free_tokens  = {'string.start': 'string.end'}
    end_free_tokens    = {'string.end': 'string.start'}
    def is_base(self, y): return y == 0
    def _is_indent(self, t): return t.name == 'spaces'
    def _is_ignored(self, t):
        return t.fqname() in ('spaces', 'eol', 'comment', 'comment.start',
                              'comment.data', 'comment.null', 'comment.end')

class JavaContext(context.Context):
    class_match   = CLASS_MATCH
    class_offset  = CLASS_OFFSET
    method_match  = METHOD_MATCH
    method_offset = METHOD_OFFSET
    def _regen_stack(self, y):
        if y > 0 and self.namelines[y - 1][1]:
            return list(self.namelines[y - 1][1])
        else:
            return []

    def _build_name_map(self, y1, y2, last, curr, stack):
        highlights = self.mode.window.get_highlighter()

        i = y1
        while i < y2:
            if not stack: curr = None
            tokens  = highlights.tokens[i]

            result = self.class_match.match(tokens)
            if result: curr = tokens[result[0] - self.class_offset].string
            result = self.method_match.match(tokens)
            if result: curr = tokens[result[0] - self.method_offset].string

            if curr is not None: self.names.setdefault(curr, i)

            for t in tokens:
                if t.match('delimiter', '{'):
                    stack.append(curr)
                elif t.match('delimiter', '}'):
                    if stack: stack.pop(-1)
                    if stack:
                        curr = stack[-1]
                    else:
                        curr = None

            if curr: self.namelines[i] = (curr, tuple(stack))
            i += 1

class Java(mode.Fundamental):
    name        = 'Java'
    extensions  = ['.java']
    tabbercls   = JavaTabber2
    grammar     = JavaGrammar
    commentc    = '//'
    opentokens  = ('delimiter',)
    opentags    = {'(': ')', '[': ']', '{': '}'}
    closetokens = ('delimiter',)
    closetags   = {')': '(', ']': '[', '}': '{'}
    colors      = {
        'java_comment.start':    ('red', 'default', 'bold'),
        'java_comment.end':      ('red', 'default', 'bold'),
        'java_comment.javadoc':  ('magenta', 'default', 'bold'),
        'java_comment.javaname': ('yellow', 'default', 'bold'),
        'java_comment.data':     ('red', 'default', 'bold'),
        'java_comment.null':     ('red', 'default', 'bold'),
        'import':                ('blue', 'default', 'bold'),
        'java_class':            ('green', 'default', 'bold'),
        'java_label':            ('magenta', 'default', 'bold'),
        'java_builtin':          ('magenta', 'default', 'bold'),
        'java_char':             ('green', 'default', 'bold'),
        'java_function':         ('blue', 'default', 'bold'),
        'java_type':             ('magenta', 'default', 'bold'),
    }

    _bindings = {
        'close-paren':   (')',),
        'close-brace':   ('}',),
        'close-bracket': (']',),
    }

    format = "%(flag)s  %(bname)-18s  (%(mname)s)  %(indent)s  %(cursor)s/%(mark)s  %(perc)s  [%(func)s]"

    def __init__(self, w):
        mode.Fundamental.__init__(self, w)
        self.context = JavaContext(self)

    def get_status_names(self):
        names = mode.Fundamental.get_status_names(self)
        c = self.window.logical_cursor()
        names['func'] = self.get_line_function(c.y)
        return names
    def get_functions(self):
        return self.context.get_names()
    def get_function_names(self):
        return self.context.get_name_list()
    def get_line_function(self, y):
        return self.context.get_line_name(y)

install = Java.install