from . import *

from .python import highlightPython

STYLES = {
    'comment': textformat('blue'),
    'keyword': textformat('brown', 'bold'),
    'argument': textformat('green'),
    'error': textformat('white', 'bg:red'),
    'skipped': textformat('gray'),
}

MULTILINE_PYTHON = 0x01000
MULTILINE_EMBED  = 0x02000
MULTILINE_SKIP   = 0x04000
MULTILINE        = 0x0F000

CONTINUED_PYTHON = 0x10000
CONTINUED        = 0xF0000

multiline_command = {
    MULTILINE_PYTHON: 'python',
    MULTILINE_EMBED: 'embed',
    MULTILINE_SKIP: 'skip',
}

multiline_state = dict((v, k) for (k, v) in multiline_command.items())


class Highlighter(QtGui.QSyntaxHighlighter):
    """
    Syntax highlighter for the PyMOL command language.
    """

    def highlightBlock(self, text):
        import re
        from pymol import cmd
        from .pmlparser import parse_pml

        state = max(0, self.previousBlockState())
        state_python = -1

        if state & MULTILINE:
            com = multiline_command[state & MULTILINE]
            if re.match(r'(_ )?[ \t]*' + com + r' end\b', text) is not None:
                self.setFormat(0, len(text), STYLES['keyword'])
                self.setCurrentBlockState(0)
            elif state & MULTILINE_PYTHON:
                state = highlightPython(self, state & ~MULTILINE, text)
                self.setCurrentBlockState(state | MULTILINE_PYTHON)
            else:
                self.setFormat(0, len(text), STYLES['skipped'])
                self.setCurrentBlockState(state)

            return

        if state & CONTINUED:
            if state & CONTINUED_PYTHON:
                state = highlightPython(self, state & ~CONTINUED, text)
                state |= CONTINUED_PYTHON

            if not text.endswith('\\'):
                state = 0

            self.setCurrentBlockState(state)
            return

        for hit in parse_pml(text):
            self.setCurrentBlockState(0)
            state_python = -1

            if hit['type'] == 'comment':
                i, j = hit['expr']
                self.setFormat(i, j - i, STYLES['comment'])
            elif hit['type'] == 'python':
                i = hit['args'][0]
                j = hit['expr'][1]
                if j > i and text[min(j, len(text)) - 1] == ':':
                    # blocks not supported
                    self.setFormat(i, j - i, STYLES['error'])
                else:
                    state_python = highlightPython(self, 0, text, i, j)
            elif hit['type'] == 'command':
                i, j = hit['command']
                self.setFormat(i, j - i, STYLES['keyword'])

                com = text[i:j]
                args_i = hit['args']
                i = args_i[0]

                # arguments
                for aa, j in zip(cmd.auto_arg, args_i[1:]):
                    aa = aa.get(com)
                    if aa is not None:
                        keywords = aa[0]().keywords if callable(aa[0]) else aa[0].keywords
                        for m in re.finditer(r'\S+', text[i:j]):
                            mi = i + m.start()
                            mj = i + m.end()
                            arg = text[mi:mj]
                            if arg in keywords:
                                self.setFormat(mi, mj - mi, STYLES['argument'])

                    i = j + 1

                # warn about semantic errors
                if com in ('@', 'run'):
                    mi, mj = args_i[:2]
                    filename = text[mi:mj]
                    badext = '.py' if com == '@' else '.pml'
                    if filename.rstrip().endswith(badext):
                        self.setFormat(mi, mj - mi, STYLES['error'])

                self.setCurrentBlockState(multiline_state.get(com, -1))

        if text.endswith('\\'):
            if state_python != -1:
                self.setCurrentBlockState(CONTINUED_PYTHON | state_python)
            else:
                self.setCurrentBlockState(CONTINUED)
