# -*- coding: utf8 -*-

# -----------------------------------------------------------------
# This file is a part of PAMPI project.
# Name:         PAMPI
# Copyright:    (C) 2017-2024 Pascal PETER
# Licence:      GNU General Public Licence version 3
# Website:      http://pascal.peter.free.fr/
# Email:        pascal.peter at free.fr
# -----------------------------------------------------------------
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# -----------------------------------------------------------------

"""
DESCRIPTION :
    un QSyntaxHighlighter adapté de celui-ci :
    https://github.com/rupeshk/MarkdownHighlighter
"""

# importation des modules utiles :
import re

import utils, utils_functions

from PyQt5 import QtCore, QtWidgets, QtGui




class markdownEditor(QtWidgets.QTextEdit):
    """
    un éditeur avec coloration syntaxique
    et reconnaissance des mots traduits.
    """
    def __init__(self, parent=None, readOnly=False):
        super(markdownEditor, self).__init__(parent)
        self.main = parent
        self.ctrl = False
        self.noChange = False
        self.readOnly = readOnly
        if readOnly:
            self.setReadOnly(True)
        self.setFontFamily('"DejaVu Sans Mono", monospace')
        self.fontPointSize = 10
        self.setFontPointSize(self.fontPointSize)
        self.highlighter = MarkdownHighlighter(self)

    def keyPressEvent(self, event):
        if not(self.readOnly):
            if event.key() == QtCore.Qt.Key_Control:
                self.ctrl = True
            elif event.key() == QtCore.Qt.Key_Tab:
                self.textCursor().insertText('    ')
                return
        QtWidgets.QTextEdit.keyPressEvent(self, event)

    def keyReleaseEvent(self, event):
        if not(self.readOnly):
            if event.key() == QtCore.Qt.Key_Control:
                self.ctrl = False
        QtWidgets.QTextEdit.keyReleaseEvent(self, event)

    def wheelEvent(self, event):
        if not(self.readOnly) and self.ctrl:
            if utils.PYQT == 'PYQT5':
                angle = event.angleDelta().y()
            else:
                angle = event.delta()
            mini = 6
            maxi = 40
            if angle > 0:
                self.fontPointSize += 1
            else:
                self.fontPointSize -= 1
            if self.fontPointSize > maxi:
                self.fontPointSize = maxi
            elif self.fontPointSize < mini:
                self.fontPointSize = mini
            self.setFontPointSize(self.fontPointSize)
            # pour esquiver l'appel à main.textChanged :
            self.noChange = True
            self.setPlainText(self.toPlainText())
        else:
            QtWidgets.QTextEdit.wheelEvent(self, event)



class MarkdownHighlighter(QtGui.QSyntaxHighlighter):
    """
    
    """

    MARKDOWN_KEYS_REGEX = {
        'step': re.compile(r'(?u)^\#{1,1}( )(.*?)\#*(\n|$)'), 
        'header': re.compile(r'(?u)^\#{2,6}( )(.*?)\#*(\n|$)'), 
        'bold' : re.compile(r'(?P<delim>\*\*)(?P<text>.+)(?P=delim)'), 
        'italic': re.compile(r'(?P<delim>\*)(?P<text>[^*]{2,})(?P=delim)'), 
        'strikeOut' : re.compile(r'(?P<delim>\~\~)(?P<text>.+)(?P=delim)'), 
        'link': re.compile(r'(?u)(^|(?P<pre>[^!]))\[.*?\]:?[ \t]*\(?[^)]+\)?'), 
        'image': re.compile(r'(?u)!\[.*?\]\(.+?\)'), 
        'unorderedList': re.compile(r'(?u)^\s*(\* |\+ |- )+\s*'), 
        'unorderedListStar': re.compile(r'^\s*(\* )+\s*'), 
        'orderedList': re.compile(r'(?u)^\s*(\d+\. )\s*'), 
        'blockQuote': re.compile(r'(?u)^\s*>+\s*'), 
        'blockQuoteCount': re.compile('^[ \t]*>[ \t]?'), 
        'codeSpan': re.compile('(?P<delim>`+).+?(?P=delim)'), 
        'codeBlock': re.compile('^([ ]{4,}|\t).*'), 
        'line': re.compile(r'(?u)^(\s*(\*|-)\s*){3,}$'), 
        'html': re.compile('<.+?>'), 
        'commentBegin': QtCore.QRegExp('<!--'), 
        'commentEnd': QtCore.QRegExp('-->'), 
        'maths': re.compile(r'(?P<delim>\$)(?P<text>[^$]{2,})(?P=delim)'), 
        }

    def __init__(self, parent):
        QtGui.QSyntaxHighlighter.__init__(self, parent)
        self.parent = parent

        self.defaultColor = '#000000'
        self.defaultBackgroundColor = '#ffffff'
        pal = self.parent.palette()
        pal.setColor(
            QtGui.QPalette.Base, QtGui.QColor(self.defaultBackgroundColor))
        self.parent.setPalette(pal)
        self.parent.setTextColor(QtGui.QColor(self.defaultColor))

        self.MARKDOWN_KWS_FORMAT = {
            'step': self.applyFormat('#3bb2dc', 'bold'), 
            'header': self.applyFormat('#2aa198', 'bold'), 
            'bold': self.applyFormat('#859900', 'bold'), 
            'italic': self.applyFormat('#b58900', 'italic'), 
            'strikeOut': self.applyFormat('#b58900', 'strikeOut'), 
            'link': self.applyFormat('#cb4b16'), 
            'image': self.applyFormat('#cb4b16'), 
            'unorderedList': self.applyFormat('#dc322f'), 
            'orderedList': self.applyFormat('#dc322f'), 
            'blockQuote': self.applyFormat('#dc322f'), 
            'codeSpan': self.applyFormat('#dc322f'), 
            'codeBlock': self.applyFormat('#ff9900'), 
            'line': self.applyFormat('#2aa198'), 
            'html': self.applyFormat('#c000c0'), 
            'comment': self.applyFormat('#aaaaaa'), 
            'maths': self.applyFormat('#000055'), 
            }

        self.rehighlight()

    def applyFormat(self, color, *args):
        """
        return a QTextCharFormat with the given attributes.
        """
        _color = QtGui.QColor()
        _color.setNamedColor(color)
        _format = QtGui.QTextCharFormat()
        _format.setForeground(_color)
        if 'bold' in args:
            _format.setFontWeight(QtGui.QFont.Bold)
        if 'italic' in args:
            _format.setFontItalic(True)
        if 'strikeOut' in args:
            _format.setFontStrikeOut(True)
        return _format

    def highlightBlock(self, text):
        text = utils_functions.u(text)
        self.highlightMarkdown(text, 0)
        self.highlightComments(text)

    def highlightMarkdown(self, text, strt):
        cursor = QtGui.QTextCursor(self.document())
        bf = cursor.blockFormat()
        self.setFormat(0, len(text), QtGui.QColor(self.defaultColor))

        #Block quotes can contain all elements so process it first
        self.highlightBlockQuote(text, cursor, bf, strt)

        #If empty line no need to check for below elements just return
        if self.highlightEmptyLine(text, cursor, bf, strt):
            return

        if self.highlightHeader(text, cursor, bf, strt):
            return

        if self.highlightStep(text, cursor, bf, strt):
            return

        self.highlightEmphasis(text, cursor, bf, strt)
        self.highlightCodeBlock(text, cursor, bf, strt)
        
        others = (
            'unorderedList', 
            'orderedList', 
            'bold', 
            'strikeOut', 
            'image', 
            'codeSpan', 
            'link', 
            'line', 
            'maths', 
            'html')
        for what in others:
            self.doHighlight(text, cursor, bf, strt, what)

    def doHighlight(self, text, cursor, bf, strt, what):
        found = False
        for mo in re.finditer(self.MARKDOWN_KEYS_REGEX[what], text):
            self.setFormat(
                mo.start() + strt, 
                mo.end() - mo.start() - strt, 
                self.MARKDOWN_KWS_FORMAT[what])
            found = True
        return found

    def highlightBlockQuote(self, text, cursor, bf, strt):
        found = False
        mo = re.search(self.MARKDOWN_KEYS_REGEX['blockQuote'], text)
        if mo:
            self.setFormat(
                mo.start(), 
                mo.end() - mo.start(), 
                self.MARKDOWN_KWS_FORMAT['blockQuote'])
            unquote = re.sub(
                self.MARKDOWN_KEYS_REGEX['blockQuoteCount'], '', text)
            spcs = re.match(
                self.MARKDOWN_KEYS_REGEX['blockQuoteCount'], text)
            spcslen = 0
            if spcs:
                spcslen = len(spcs.group(0))
            self.highlightMarkdown(unquote, spcslen)
            found = True
        return found

    def highlightEmptyLine(self, text, cursor, bf, strt):
        textAscii = utils_functions.u(text).replace('\u2029', '\n')
        if textAscii.strip():
            return False
        else:
            return True

    def highlightHeader(self, text, cursor, bf, strt):
        found = False
        for mo in re.finditer(self.MARKDOWN_KEYS_REGEX['header'], text):
            self.setFormat(
                mo.start() + strt, 
                mo.end() - mo.start(), 
                self.MARKDOWN_KWS_FORMAT['header'])
            found = True
        return found

    def highlightStep(self, text, cursor, bf, strt):
        found = False
        for mo in re.finditer(self.MARKDOWN_KEYS_REGEX['step'], text):
            self.setFormat(
                mo.start() + strt, 
                mo.end() - mo.start(), 
                self.MARKDOWN_KWS_FORMAT['step'])
            found = True
        return found

    def highlightEmphasis(self, text, cursor, bf, strt):
        found = False
        unlist = re.sub(
            self.MARKDOWN_KEYS_REGEX['unorderedListStar'], '', text)
        spcs = re.match(
            self.MARKDOWN_KEYS_REGEX['unorderedListStar'], text)
        spcslen = 0
        if spcs:
            spcslen = len(spcs.group(0))
        for mo in re.finditer(self.MARKDOWN_KEYS_REGEX['italic'], unlist):
            self.setFormat(
                mo.start() + strt + spcslen, 
                mo.end() - mo.start() - strt, 
                self.MARKDOWN_KWS_FORMAT['italic'])
            found = True
        return found

    def highlightCodeBlock(self, text, cursor, bf, strt):
        found = False
        for mo in re.finditer(self.MARKDOWN_KEYS_REGEX['codeBlock'], text):
            stripped = text.lstrip()
            if stripped[0] not in ('*', '-', '+', '>'):
                self.setFormat(
                    mo.start() + strt, 
                    mo.end() - mo.start(), 
                    self.MARKDOWN_KWS_FORMAT['codeBlock'])
                found = True
        return found

    def highlightComments(self, text):

        self.setCurrentBlockState(0)
        startIndex = 0
        if self.previousBlockState() != 1:
            startIndex = self.MARKDOWN_KEYS_REGEX[
                'commentBegin'].indexIn(text)

        while startIndex >= 0:
            endIndex = self.MARKDOWN_KEYS_REGEX[
                'commentEnd'].indexIn(text, startIndex)
            if endIndex == -1:
                self.setCurrentBlockState(1)
                commentml_lg = len(text) - startIndex
            else:
                commentml_lg = endIndex-startIndex + self.MARKDOWN_KEYS_REGEX[
                    'commentEnd'].matchedLength()
            self.setFormat(
                startIndex, 
                commentml_lg, 
                self.MARKDOWN_KWS_FORMAT['comment'])
            startIndex = self.MARKDOWN_KEYS_REGEX[
                'commentBegin'].indexIn(text, startIndex+commentml_lg)

