#!/usr/bin/python
# -*- coding: utf-8 -*-

import re

from PyQt5.QtCore import Qt
from PyQt5.QtGui import QBrush, QTextCursor, QColor, QFont, QSyntaxHighlighter
from PyQt5.QtGui import QTextBlockFormat, QTextCharFormat

import manuskript.models.references as Ref
import manuskript.ui.style as S
from manuskript import settings
from manuskript import functions as F

import logging
LOGGER = logging.getLogger(__name__)

class BasicHighlighter(QSyntaxHighlighter):
    def __init__(self, editor):
        QSyntaxHighlighter.__init__(self, editor.document())

        self.editor = editor
        self._defaultBlockFormat = QTextBlockFormat()
        self._defaultCharFormat = QTextCharFormat()
        self.defaultTextColor = QColor(S.text)
        self.backgroundColor = QColor(S.base)
        self.markupColor = QColor(S.textLight)
        self.linkColor = QColor(S.link)
        self.spellingErrorColor = QColor(Qt.red)

        # Matches during checking can be separated by their type (all of them listed here):
        # https://languagetool.org/development/api/org/languagetool/rules/ITSIssueType.html
        #
        # These are the colors for actual spell-, grammar- and style-checking:
        self._errorColors = {
            'addition' : QColor(255, 215, 0),               # gold
            'characters' : QColor(135, 206, 235),           # sky blue
            'duplication' : QColor(0, 255, 255),            # cyan / aqua
            'formatting' : QColor(0, 128, 128),             # teal
            'grammar' : QColor(0, 0, 255),                  # blue
            'inconsistency' : QColor(128, 128, 0),          # olive
            'inconsistententities' : QColor(46, 139, 87),   # sea green
            'internationalization' : QColor(255, 165, 0),   # orange
            'legal' : QColor(255, 69, 0),                   # orange red
            'length' : QColor(47, 79, 79),                  # dark slate gray
            'localespecificcontent' : QColor(188, 143, 143),# rosy brown
            'localeviolation' : QColor(128, 0, 0),          # maroon
            'markup' : QColor(128, 0, 128),                 # purple
            'misspelling' : QColor(255, 0, 0),              # red
            'mistranslation' : QColor(255, 0, 255),         # magenta / fuchsia
            'nonconformance' : QColor(255, 218, 185),       # peach puff
            'numbers' : QColor(65, 105, 225),               # royal blue
            'omission' : QColor(255, 20, 147),              # deep pink
            'other' : QColor(138, 43, 226),                 # blue violet
            'patternproblem' : QColor(0, 128, 0),           # green
            'register' : QColor(112,128,144),               # slate gray
            'style' : QColor(0, 255, 0),                    # lime
            'terminology' : QColor(0, 0, 128),              # navy
            'typographical' : QColor(255, 255, 0),          # yellow
            'uncategorized' : QColor(128, 128, 128),        # gray
            'untranslated' : QColor(210, 105, 30),          # chocolate
            'whitespace' : QColor(192, 192, 192)            # silver
        }

    def setDefaultBlockFormat(self, bf):
        self._defaultBlockFormat = bf
        self.rehighlight()

    def setDefaultCharFormat(self, cf):
        self._defaultCharFormat = cf
        self.rehighlight()

    def setMisspelledColor(self, color):
        self._errorColors['misspelled'] = color

    def updateColorScheme(self, rehighlight=True):
        """
        Generates a base set of colors that will take account of user
        preferences, and use system style.
        """

        # Reading user settings
        opt = settings.textEditor

        if not self.editor._fromTheme or not self.editor._themeData:

            self.defaultTextColor = QColor(opt["fontColor"])
            self.backgroundColor = (QColor(opt["background"])
                                    if not opt["backgroundTransparent"]
                                    else QColor(S.window))
            self.markupColor = F.mixColors(self.defaultTextColor,
                                           self.backgroundColor,
                                           .3)
            self.linkColor = QColor(S.link)
            self.spellingErrorColor = QColor(opt["misspelled"])
            self._defaultCharFormat.setForeground(QBrush(self.defaultTextColor))

        # FullscreenEditor probably
        else:
            opt = self.editor._themeData
            self.defaultTextColor = QColor(opt["Text/Color"])
            self.backgroundColor =  F.mixColors(
                QColor(opt["Foreground/Color"]),
                QColor(opt["Background/Color"]),
                int(opt["Foreground/Opacity"])/100.)
            self.markupColor = F.mixColors(self.defaultTextColor,
                                           self.backgroundColor,
                                           .3)
            self.linkColor = QColor(S.link)
            self.spellingErrorColor = QColor(opt["Text/Misspelled"])

        if rehighlight:
            self.rehighlight()

    def highlightBlock(self, text):
        """Apply syntax highlighting to the given block of text.
        """
        self.highlightBlockBefore(text)
        self.doHighlightBlock(text)
        self.highlightBlockAfter(text)

    def doHighlightBlock(self, text):
        """
        Virtual function to subclass.
        """
        pass

    def highlightBlockBefore(self, text):
        """Highlighting to do before anything else.

        When subclassing BasicHighlighter, you must call highlightBlockBefore
        before you do any custom highlighting. Or implement doHighlightBlock.
        """

        #LOGGER.debug("undoSteps before: %s", self.currentBlock().document().availableUndoSteps())
        c = QTextCursor(self.currentBlock())
        #c.joinPreviousEditBlock()
        bf = QTextBlockFormat(self._defaultBlockFormat)
        if bf != c.blockFormat():
            c.setBlockFormat(bf)
        #c.endEditBlock()
        #LOGGER.debug("undoSteps after: %s", self.currentBlock().document().availableUndoSteps())

        # self.setFormat(0, len(text), self._defaultCharFormat)

    def highlightBlockAfter(self, text):
        """Highlighting to do after everything else.

        When subclassing BasicHighlighter, you must call highlightBlockAfter
        after your custom highlighting. Or implement doHighlightBlock.
        """

        # References
        for txt in re.finditer(Ref.RegEx, text):
            fmt = self.format(txt.start())
            fmt.setFontFixedPitch(True)
            fmt.setFontWeight(QFont.DemiBold)

            if txt.group(1) == Ref.TextLetter:
                fmt.setBackground(QBrush(Ref.TextHighlightColor))
            elif txt.group(1) == Ref.CharacterLetter:
                fmt.setBackground(QBrush(Ref.CharacterHighlightColor))
            elif txt.group(1) == Ref.PlotLetter:
                fmt.setBackground(QBrush(Ref.PlotHighlightColor))
            elif txt.group(1) == Ref.WorldLetter:
                fmt.setBackground(QBrush(Ref.WorldHighlightColor))

            self.setFormat(txt.start(),
                           txt.end() - txt.start(),
                           fmt)

        if hasattr(self.editor, "spellcheck") and self.editor.spellcheck and self.editor._dict:
            # Spell checking

            # Following algorithm would not check words at the end of line.
            # This hacks adds a space to every line where the text cursor is not
            # So that it doesn't spellcheck while typing, but still spellchecks at
            # end of lines. See github's issue #166.
            textedText = text
            if self.currentBlock().position() + len(text) != \
               self.editor.textCursor().position():
                textedText = text + " "

            # The text should only be checked once as a whole
            for match in self.editor._dict.checkText(textedText):
                if match.locqualityissuetype in self._errorColors:
                    highlight_color = self._errorColors[match.locqualityissuetype]

                    format = self.format(match.start)
                    format.setUnderlineColor(highlight_color)
                    # SpellCheckUnderline fails with some fonts
                    format.setUnderlineStyle(QTextCharFormat.WaveUnderline)
                    self.setFormat(match.start, match.end - match.start, format)
