import mode, tab
from lex import Grammar, PatternRule, NocasePatternRule, RegionRule, NocaseRegionRule
from mode.python import StringGrammar

class PlPgSqlGrammar(Grammar):
    rules = [
        PatternRule(r'comment', r'--.*\n$'),
        RegionRule(r'comment', '/\*', Grammar, '\*/'),
        PatternRule(r'delimiter', r':=|[():;,\.\$\[\]]'),
    
        NocasePatternRule(r'attribute', r'(?:check|exists|unique|not null|default|primary key|minvalue|foreign key|references)(?![A-Za-z0-9_])'),
        NocasePatternRule(r'keyword', r'(?:declare|begin|end|raise notice|return)'),
        NocasePatternRule(r'operator', r'(?:case|when|then|else|end|not|and|or|is not|is|in|not in)(?![A-Za-z0-9_])'),
        NocasePatternRule(r'keyword', r'(?:create database|create index|create sequence|create table|create trigger|create view|select|insert|update|delete|drop database|drop index|drop sequence|drop table|drop trigger|drop view|create user|alter user|drop user|drop function|grant|revoke|create function|create or replace function|create or replace view|create language|create operator|create type)(?![A-Za-z0-9_])'),
        NocasePatternRule(r'pseudokeyword', r'(?:returns|language|right join|left join|inner join|outer join|join|where|null|true|false|into|values|as|from|order by|asc|desc|limit|distinct|cascade|using|on)(?![A-Za-z0-9_])'),
        NocasePatternRule(r'type', r'(?:void|row|serial|varchar|float|integer|int|text|timestamptz|timestamp|datetz|date|timetz|time|boolean|bool)(?![A-Za-z0-9_])'),
        PatternRule(r'builtin', r'(?:nextval|current_timestamp|current_time|current_date)(?![A-Za-z0-9_])'),
        RegionRule(r'string', "''", StringGrammar, "''"),
        RegionRule(r'quoted', '"', StringGrammar, '"'),
        PatternRule(r'bareword', r'[A-Za-z0-9_]+'),
        PatternRule(r'empty', r'^ *\n$'),
        PatternRule(r'eol', r'\n'),
    ]

class FunctionGrammar(Grammar):
    rules = [
        PatternRule(r'comment', r'--.*\n$'),
        RegionRule(r'comment', '/\*', Grammar, '\*/'),
        PatternRule(r'delimiter', r':=|[():;,\.\$\[\]]'),
        
        PatternRule(r'name', r'[a-zA-Z_][a-zA-Z0-9_]*(?=\()'),
        NocasePatternRule(r'keyword', r'(?:as|returns|language)'),
        NocasePatternRule(r'type', r'(?:void|row|serial|varchar|float|integer|int|text|timestamptz|timestamp|datetz|date|timetz|time|boolean|bool)(?![A-Za-z0-9_])'),
    
        NocasePatternRule(r'language', r'(?<=language ) *[a-zA-Z_][a-zA-Z0-9_]+'),
    
        RegionRule(r'definition', "'", PlPgSqlGrammar, "'(?!')"),
        PatternRule(r'bareword', r'[A-Za-z0-9_]+'),
        PatternRule(r'empty', r'^ *\n$'),
        PatternRule(r'eol', r'\n'),
    ]

class SqlGrammar(Grammar):
    rules = [
        PatternRule(r'comment', r'--.*\n$'),
        RegionRule(r'comment', '/\*', Grammar, '\*/'),
        PatternRule(r'delimiter', r':=|[():;,\.\$\[\]]'),
    
        NocaseRegionRule(r'function', r'create function', FunctionGrammar, r';'),
        NocaseRegionRule(r'function', r'create or replace function', FunctionGrammar, r';'),
    
        NocasePatternRule(r'attribute', r'(?:check|exists|unique|not null|default|primary key|minvalue|foreign key|references)(?![A-Za-z0-9_])'),
        NocasePatternRule(r'operator', r'(?:case|when|then|else|end|not|and|or|is not|is|in|not in)(?![A-Za-z0-9_])'),
        NocasePatternRule(r'keyword', r'(?:create database|create index|create sequence|create table|create trigger|create view|select|insert|update|delete|drop database|drop index|drop sequence|drop table|drop trigger|drop view|create user|alter user|drop user|drop function|grant|revoke|create function|create or replace function|create or replace view|create language|create operator|create type)(?![A-Za-z0-9_])'),
        NocasePatternRule(r'pseudokeyword', r'(?:returns|language|right join|left join|inner join|outer join|join|where|null|true|false|into|values|as|from|order by|asc|desc|limit|distinct|cascade|using|on)(?![A-Za-z0-9_])'),
        NocasePatternRule(r'type', r'(?:void|row|serial|varchar|float|integer|int|text|timestamptz|timestamp|datetz|date|timetz|time|boolean|bool)(?![A-Za-z0-9_])'),
        PatternRule(r'builtin', r'(?:nextval|current_timestamp|current_time|current_date)(?![A-Za-z0-9_])'),
        RegionRule(r'string', "'", StringGrammar, "'"),
        RegionRule(r'quoted', '"', StringGrammar, '"'),
        PatternRule(r'bareword', r'[A-Za-z0-9_]+'),
        PatternRule(r'empty', r'^ *\n$'),
        PatternRule(r'eol', r'\n'),
    ]

class SqlTabber(tab.StackTabber):
    wst  = ('null', 'eol',)
    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 == 'function'
    def _handle_other_token(self, currlvl, y, i):
        token  = self.get_token(y, i)
        if token.name == 'delimiter' and token.string == ';':
            self._opt_pop('cont')
        elif token.name == 'keyword':
            if token.string == 'declare':
                self._opt_append('declare', currlvl + 4)
            elif token.string == 'begin':
                currlvl -= 4
            elif token.string == 'end':
                self._opt_pop('declare')
                currlvl = self.get_curr_level()

        if self.is_rightmost_token(y, i):
            if not self._empty() and token.name == 'continuation':
                self._opt_append('cont', currlvl + 4)
            elif token.name == 'eol' and not self.markers:
                self._opt_pop("cont")
        return currlvl

class Sql(mode.Fundamental):
    modename    = 'Sql'
    extensions  = ['.sql']
    grammar     = SqlGrammar
    tabbercls   = SqlTabber
    opentokens  = ('delimiter',)
    opentags    = {'(': ')', '[': ']', '{': '}'}
    closetokens = ('delimiter',)
    closetags   = {')': '(', ']': '[', '}': '{'}
    colors      = {
        'comment':        ('red', 'default'),
        'operator':       ('yellow', 'default'),
        'attribute':      ('magenta', 'default'),
        'keyword':        ('cyan', 'default'),
        'pseudokeyword':  ('cyan', 'default'),
        'type':           ('green', 'default'),
        'builtin':        ('yellow', 'default'),
        'quoted':         ('yellow', 'default'),
        'string.start':   ('green', 'default'),
        'string.null':    ('green', 'default'),
        'string.escaped': ('magenta', 'default'),
        'string.octal':   ('magenta', 'default'),
        'string.end':     ('green', 'default'),
        'bareword':       ('default', 'default'),

        'function.start':    ('cyan', 'default'),
        'function.null':     ('default', 'default'),
        'function.name':     ('magenta', 'default'),
        'function.language': ('magenta', 'default'),
        'function.end':      ('default', 'default'),

        'function.definition.start':    ('magenta', 'default'),
        'function.definition.bareword': ('magenta', 'default'),
        'function.definition.null':     ('magenta', 'default'),
        'function.definition.end':      ('magenta', '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 = Sql.install