import color, method, mode
from lex import Grammar, Rule, PatternRule, RegionRule, PatternMatchRule

class StringGrammar1(Grammar):
    rules = [
        PatternRule(r'data', r'[^"&]+'),
        PatternRule(r'escaped', r'&[a-z]+;'),
    ]
class StringGrammar2(Grammar):
    rules = [
        PatternRule(r'data', r"[^'&]+"),
        PatternRule(r'escaped', r'&[a-z]+;'),
    ]

class CDataGrammar(Grammar):
    rules = [PatternRule(r'data', r'(?:[^\]]|\](?!\])|\]\](?!>))+')]
class CommentGrammar(Grammar):
    rules = [PatternRule(r'data', r'(?:[^-]|-(?!-)|--(?!>))+')]
class MetadataGrammar(Grammar):
    rules = [PatternRule(r'data', r'(?:[^?]|\?(?!>))+')]

class TagGrammar(Grammar):
    rules = [
        PatternRule(r'attrname', r'[a-zA-Z_][a-zA-Z0-9_]+(?==)'),
        PatternRule(r'namespace', r'[a-zA-Z_]+(?=:)'),
        PatternRule(r'name', r'[a-zA-Z_][a-zA-Z0-9_]*'),
        PatternRule(r'delimiter', r'[:/=]'),
        RegionRule(r'string', r'"', StringGrammar1, r'"'),
        RegionRule(r'string', r"'", StringGrammar2, r"'"),
        PatternRule(r'spaces', r' +'),
        PatternRule(r'eol', r'\n'),
    ]

class XMLGrammar(Grammar):
    rules = [
        # TODO: how does cdata work again?
        PatternRule(r'data', r'[^<& \n]+'),
        PatternRule(r'spaces', r' +'),
        PatternRule(r'xml.entity', r'&[a-z]+;'),
        PatternRule(r'eol', r'\n'),
        PatternMatchRule('x', r'(<)(/)([a-zA-Z_][a-zA-Z0-9_]*)(>)',
                         'xml.tag.start', 'xml.tag.delimiter', 'xml.tag.name',
                         'xml.tag.end'),
        RegionRule(r'xml.tag', r'<(?![\?!])', TagGrammar, r'/?>'),
        RegionRule(r'comment', r'<!--', CommentGrammar, r'-->'),
        RegionRule(r'xml.metadata', r'<\?', MetadataGrammar, r'\?>'),
        RegionRule(r'xml.cdata', r'<!\[CDATA\[', CDataGrammar, r'\]\]>'),
    ]

class XmlValidate(method.shell.Exec):
    '''Valid the buffer's contents as valid XML.'''
    show_success = True
    args         = []
    def _execute(self, w, **vargs):
        self._doit(w, w.buffer.path, 'xmlwf %(path)r', cmdname='xml-validate')

class XmlCreateTag(method.Method):
    '''Create an opening and closing tag'''
    args = [method.Argument('tagname', prompt="Tag Name: ",
                            help="Create an open/close tag pair")]
    def _execute(self, w, **vargs):
        t = vargs['tagname']
        w.insert_string_at_cursor("<%s>" % t)
        p = w.logical_cursor()
        w.insert_string_at_cursor("</%s>" % t)
        w.goto(p)

class XmlCreateComment(method.Method):
    '''Create an opening and closing tag'''
    def _execute(self, w, **vargs):
        w.insert_string_at_cursor("<!-- ")
        p = w.logical_cursor()
        w.insert_string_at_cursor(" -->")
        w.goto(p)

class XmlCreateCdata(method.Method):
    '''Create an opening and closing tag'''
    def _execute(self, w, **vargs):
        w.insert_string_at_cursor("<![CDATA[")
        p = w.logical_cursor()
        w.insert_string_at_cursor("]]>")
        w.goto(p)

class XML(mode.Fundamental):
    name       = 'XML'
    extensions = ['.xml', '.xml.in', '.xsl', '.xsd']
    grammar    = XMLGrammar
    colors     = {
        'xml.metadata.start':   ('magenta', 'default', 'bold'),
        'xml.metadata.data':    ('magenta', 'default', 'bold'),
        'xml.metadata.end':     ('magenta', 'default', 'bold'),
        'xml.tag.start':        ('default', 'default', 'bold'),
        'xml.tag.namespace':    ('magenta', 'default', 'bold'),
        'xml.tag.name':         ('blue', 'default', 'bold'),
        'xml.tag.attrname':     ('cyan', 'default', 'bold'),
        'xml.tag.end':          ('default', 'default', 'bold'),
        'xml.entity':           ('magenta', 'default', 'bold'),
        'xml.cdata.start':      ('magenta', 'default', 'bold'),
        'xml.cdata.data':       ('green', 'default', 'bold'),
        'xml.cdata.end':        ('magenta', 'default', 'bold'),
    }
    actions = [XmlValidate, XmlCreateTag, XmlCreateComment, XmlCreateCdata]
    _bindings = {
        'xml-create-tag': ('M-t',),
    }
install = XML.install