import os.path
import time
import tab
from mode import Fundamental
from method.shell import Interact
from lex import Grammar, PatternRule, RegionRule, PatternMatchRule
from point import Point

class StringGrammar1(Grammar):
    rules = [
        PatternRule('octal', r'\\[0-7]{3}'),
        PatternRule('hex', r'\\x[0-9a-fA-F]{2}'),
        PatternRule('escaped', r'\\.'),
        PatternRule('data', r'[^\\\']+'),
    ]
class StringGrammar2(Grammar):
    rules = [
        PatternRule('octal', r'\\[0-7]{3}'),
        PatternRule('hex', r'\\x[0-9a-fA-F]{2}'),
        PatternRule('escaped', r'\\.'),
        PatternRule('data', r'[^\\\"]+'),
    ]
class RegexGrammar(Grammar):
    rules = [
        PatternRule('octal', r'\\[0-7]{3}'),
        PatternRule('hex', r'\\x[0-9a-fA-F]{2}'),
        PatternRule('escaped', r'\\.'),
        PatternRule('data', r'[^/\\]+'),
    ]

chr1 = '[a-zA-Z_]'
chr2 = '[a-zA-Z_0-9]'
word = chr1 + chr2 + '*'

class JavascriptGrammar(Grammar):
    rules = [
        PatternRule('comment', '//.*$'),
        RegionRule('comment', '/\*', Grammar, '\*/'),
        PatternRule('continuation', r'\\(?= *$)'),

        PatternMatchRule('x', '(function)( +)(' + word + ')',
                         'js.reserved', 'spaces', 'js.function'),
        PatternMatchRule('x', '(class|new)( +)(' + word + ')',
                         'js.reserved', 'spaces', 'js.class'),
        
        PatternRule('js.reserved', '(?:abstract|as|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|goto|if|import|implements|include|instanceof|interface|in|is|namespace|native|new|null|package|private|protected|public|return|set|super|switch|synchronized|this|throws|throw|transient|true|try|typeof|use|var|void|volatile|while|with)(?!' + chr2 + ')'),

        PatternRule('js.identifier', word),
        PatternRule('js.integer', "(?:0|[1-9][0-9]*|0[0-7]+|0[xX][0-9a-fA-F]+)[lL]?"),
        PatternRule('js.float', r"[0-9]+\.[0-9]*|\.[0-9]+|(?:[0-9]|[0-9]+\.[0-9]*|\.[0-9]+)[eE][\+-]?[0-9]+"),

        PatternRule('eol', r'\n'),
        PatternRule('spaces', ' +'),
        PatternRule('delimiter', r'%=|&&=|&=|\(|\)|\*=|\+=|,|-=|\.{3}|\.|/=(?= |$)|::|:|;|<<=|>>=|>>>=|\?|\[|\]|^=|^^=|\{|\}|\|=|\|\|='),

        # fucking javascript!
        # their lexer grammar requires one-token look-behind in order to know
        # whether a "/" starts a literal regex, or is part of a mathematical
        # expression/assignment.
        RegionRule('js.regex', r"(?<=[\(=:,]) */", RegexGrammar, "/[a-z]*"),
        PatternRule('js.operator', r'!==|!=|!|%|&&|&|\*|\+\+|\+|--|-|/|<<=|<<|<=|<|===|==|=|>>>=|>>>|>>=|>>|>=|>|\\|\|\|'),

        RegionRule('string', "'", StringGrammar1, "'"),
        RegionRule('string', '"', StringGrammar2, '"'),
    ]

class JavascriptTabber2(tab.StackTabber2):
    open_tokens        = {'delimiter': {'{': '}', '(': ')', '[': ']'}}
    close_tokens       = {'delimiter': {'}': '{', ')': '(', ']': '['}}
    control_tokens     = {'js.keyword': {'if': 1, 'else': 1, 'while': 1,
                                         'do': 1, 'for': 1}}
    end_at_eof         = False
    end_at_tokens      = {'delimiter': {';': 1}}
    nocontinue_tokens  = {'delimiter': {';': 1, ',': 1},
                          'comment': 1,
                          'comment.start': 1,
                          'comment.data': 1,
                          'comment.end': 1}
    start_free_tokens  = {'string.start': 'string.end'}
    end_free_tokens    = {'string.end': 'string.start'}
    def is_base(self, y):
        if y == 0: return True
        highlighter = self.mode.window.buffer.highlights[self.mode.name]
        if not highlighter.tokens[y]: return False
        t = highlighter.tokens[y][0]
        return t.name == 'js.reserved' and t.string == 'function'
    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 RhinoStart(Interact):
    args = []
    reuse = True
    def _execute(self, w, **vargs):
        cmd = w.application.config.get('rhino.cmd', 'rhino')
        Interact._execute(self, w, bname='*Rhino*', cmd=cmd)

class RhinoLoadFile(RhinoStart):
    args = []
    reuse = True
    def _execute(self, w, **vargs):
        RhinoStart._execute(self, w, **vargs)
        b = w.application.get_buffer_by_name('*Rhino*')
        path = os.path.realpath(w.buffer.path)
        time.sleep(0.5)
        b.pipe_write('load("env.js");\n')
        time.sleep(0.5)
        b.pipe_write('load("%s");\n' % path)

class Javascript(Fundamental):
    name        = 'Javascript'
    extensions  = ['.js']
    grammar     = JavascriptGrammar
    tabbercls   = JavascriptTabber2
    commentc    = '//'
    opentokens  = ('delimiter',)
    opentags    = {'(': ')', '[': ']', '{': '}'}
    closetokens = ('delimiter',)
    closetags   = {')': '(', ']': '[', '}': '{'}
    colors      = {
        'js.function':      ('blue', 'default', 'bold'),
        'js.class':         ('magenta', 'default', 'bold'),
        'js.reserved':      ('cyan', 'default', 'bold'),
        'js.regex.start':   ('cyan', 'default', 'bold'),
        'js.regex.data':    ('cyan', 'default', 'bold'),
        'js.regex.null':    ('cyan', 'default', 'bold'),
        'js.regex.octal':   ('magenta', 'default', 'bold'),
        'js.regex.escaped': ('magenta', 'default', 'bold'),
        'js.regex.end':     ('cyan', 'default', 'bold'),
    }
    config = {'rhino.cmd': 'rhino'}
    actions = [RhinoStart, RhinoLoadFile]
    _bindings = {
        'close-paren':   (')',),
        'close-brace':   ('}',),
        'close-bracket': (']',),
    }

install = Javascript.install