import commands, os.path, string, sys, traceback
import color, completer, default, mode, regex, tab
from point import Point
from lex import Grammar, PatternRule, RegionRule, OverridePatternRule
from lex import NocasePatternRule, PatternMatchRule
from method.shell import Interact
from etags import TagManager

class AtomGrammar(Grammar):
    rules = [
        NocasePatternRule(r'escaped', r'\\(?:[bdefnrstv\\"\']|\^[a-z]|\d{1,3})'),
        NocasePatternRule(r'data', r"[^\\']+"),
    ]

class StringGrammar(Grammar):
    rules = [
        NocasePatternRule(r'escaped', r'\\(?:[bdefnrstv\\"\']|\^[a-z]|\d{1,3})'),
        NocasePatternRule(r'data', r'[^\\"]+'),
    ]

chr1 = '[a-z]'
chr2 = '[a-zA-Z0-9_@]'
atom = chr1 + chr2 + '*'

class ErlangGrammar(Grammar):
    rules = [
        PatternRule('eol', r'\n'),
        PatternRule('comment', '%.*$'),
        PatternRule('erl_attribute', '-' + atom),
        RegionRule('erl_attribute', "-'", AtomGrammar ,"'"),
        PatternRule('erl_reserved', "(?:xor|when|try|rem|receive|query|orelse|or|of|not|let|if|fun|end|div|cond|catch|case|bxor|bsr|bsl|bor|bnot|begin|band|andalso|and|after)(?!" + chr2 + ")"),
        PatternRule('erl_arity', atom + '/(?:0|[1-9][0-9]*)'),
        PatternMatchRule('x', '('+atom+')( *)(\()', 'erl_function', 'spaces', 'delimiter'),
        PatternMatchRule('x', '('+atom+')(:)', 'erl_namespace', 'delimiter'),
        PatternRule('erl_builtin', "(?:whereis|unregister|unlink|tuple_to_list|tuple_size|trunc|tl|time|throw|term_to_binary|term_to_binary|statistics|split_binary|spawn_opt|spawn_opt|spawn_opt|spawn_opt|spawn_monitor|spawn_monitor|spawn_link|spawn_link|spawn_link|spawn_link|spawn|spawn|spawn|spawn|size|setelement|self|round|registered|register|put|purge_module|process_info|process_info|process_flag|process_flag|processes|pre_loaded|port_control|port_connect|port_command|port_close|pid_to_list|open_port|now|nodes|nodes|node|node|monitor_node|module_loaded|make_ref|load_module|list_to_tuple|list_to_pid|list_to_integer|list_to_float|list_to_existing_atom|list_to_bitstring|list_to_binary|list_to_atom|link|length|is_tuple|is_reference|is_record|is_record|is_process_alive|is_port|is_pid|is_number|is_list|is_integer|is_function|is_function|is_float|is_boolean|is_bitstring|is_binary|is_atom|is_alive|iolist_to_binary|iolist_size|iolist|iodata|integer_to_list|hd|halt|halt|group_leader|group_leader|get_keys|get|get|garbage_collect|garbage_collect|float_to_list|float|ext_binary|exit|exit|erase|erase|ence|element|disconnect_node|delete_module|date|concat_binary|check_process_code|byte_size|bitstring_to_list|bit_size|binary_to_term|binary_to_list|binary_to_list|atom_to_list|atom_to_list|apply|apply|apply|abs)(?!" + chr2 + ")"),
        RegionRule('erl_atom', "'", AtomGrammar, "'"),
        RegionRule('string', '"', StringGrammar, '"'),
        PatternRule('erl_atom', atom),
        PatternRule('erl_variable', r"[A-Z_]" + chr2 + "*"),
        PatternRule('delimiter', r'->|<<|>>|\(|\)|{|}|\[|\]|\.|;|,|\|'),
        PatternRule('operator', r'\+\+|--|==|/=|=<|<|>=|>|=:=|=/=>|-|\+|\*|/|:|#|!]'),
        NocasePatternRule('number', r'\$.|[+-]?\d+#[a-z0-9]+|[+-]?\d+\.\d+|[+-]?\.\d+|[+-]?\d+'),
    ]

class ErlangTabber(tab.StackTabber):
    enterlvl = ('after', 'begin', 'case', 'catch', 'fun', 'if', 'query', 'receive')
    def _handle_close_token(self, currlvl, y, i):
        self._opt_pop(*self.enterlvl)
        return tab.StackTabber._handle_close_token(self, currlvl, y, i)
    def _handle_other_token(self, currlvl, y, i):
        token  = self.get_token(y, i)
        rtoken = self.get_next_right_token(y, i)
        ltoken = self.get_next_left_token(y, i)

        if token.name == 'eol':
            pass
        elif token.name == 'string.start':
            self._append('string', None)
        elif token.name == 'string.end':
            self._opt_pop('string')
        elif token.name == 'delimiter':
            if token.string == '->':
                if rtoken is None or rtoken.name == 'comment':
                    level = self.get_curr_level() + self.mode.tabwidth
                else:
                    level = rtoken.x
                self._append(token.string, level)
            elif token.string == '.':
                self.markers = []
            elif token.string == ';':
                self._pop()
        elif token.name == 'erl_reserved':
            if token.string in self.enterlvl:
                if rtoken is None or rtoken.name == 'comment':
                    level = self.get_curr_level() + self.mode.tabwidth
                else:
                    level = rtoken.x
                self._append(token.string, level)
            elif token.string == 'of':
                self._opt_pop('case')
            elif token.string == 'end':
                self._opt_pop('->')
                self._pop_unless('->')
                currlvl = self.get_curr_level()
        return currlvl

class ErlStart(Interact):
    args  = []
    reuse = True
    def _execute(self, w, **vargs):
        Interact._execute(self, w, bname='*Erl*', cmd='erl')

class ErlangTagManager(TagManager):
    lang  = 'Erlang'
    exts  = set(('.erl',))

class Erlang(mode.Fundamental):
    name        = 'Erlang'
    extensions  = ['.erl']
    tabwidth    = 4
    tabbercls   = ErlangTabber
    tagcls      = ErlangTagManager
    grammar     = ErlangGrammar
    commentc    = '%'
    opentokens  = ('delimiter',)
    opentags    = {'(': ')', '[': ']', '{': '}'}
    closetokens = ('delimiter',)
    closetags   = {')': '(', ']': '[', '}': '{'}
    colors      = {
        'erl_attribute':  ('cyan', 'default', 'bold'),
        'erl_reserved':   ('cyan', 'default', 'bold'),
        'erl_arity':      ('magenta', 'default', 'bold'),
        'erl_namespace':  ('magenta', 'default', 'bold'),
        'erl_function':   ('blue', 'default', 'bold'),
        'erl_builtin':    ('magenta', 'default', 'bold'),
        'erl_variable':   ('yellow', 'default', 'bold'),
        'erl_operator':   ('default', 'default', 'bold'),
        'erl_atom':       ('magenta', 'default', 'bold'),
        'erl_atom.start': ('magenta', 'default', 'bold'),
        'erl_atom.null':  ('magenta', 'default', 'bold'),
        'erl_atom.data':  ('magenta', 'default', 'bold'),
        'erl_atom.end':   ('magenta', 'default', 'bold'),
    }
    actions = [
        ErlStart,
    ]
    _bindings = {
        'close-paren':   (')',),
        'close-brace':   ('}',),
        'close-bracket': (']',),
    }

install = Erlang.install