#!/usr/bin/env python
# --!-- coding: utf8 --!--
import re

from PyQt5.QtCore import Qt, QRect, QEvent, QCoreApplication
from PyQt5.QtGui import QPalette, QFontMetrics, QKeySequence
from PyQt5.QtWidgets import QWidget, qApp, QListWidgetItem, QStyledItemDelegate, QStyle, QLabel, QToolTip, QShortcut


from manuskript.functions import mainWindow
from manuskript.ui import style
from manuskript.ui.search_ui import Ui_search
from manuskript.enums import Model

from manuskript.models.flatDataModelWrapper import flatDataModelWrapper
from manuskript.ui.searchMenu import searchMenu
from manuskript.ui.highlighters.searchResultHighlighters.searchResultHighlighter import searchResultHighlighter
import logging
LOGGER = logging.getLogger(__name__)


class search(QWidget, Ui_search):
    def __init__(self, parent=None):
        _translate = QCoreApplication.translate

        QWidget.__init__(self, parent)
        self.setupUi(self)

        self.searchTextInput.returnPressed.connect(self.search)
        self.searchTextInput.textChanged.connect(self.updateSearchFeedback)

        self.searchMenu = searchMenu()
        self.btnOptions.setMenu(self.searchMenu)
        self.searchMenu.triggered.connect(self.onSearchMenuChange)

        self.delegate = listResultDelegate(self)
        self.result.setItemDelegate(self.delegate)
        self.result.setMouseTracking(True)
        self.result.itemClicked.connect(self.openItem)

        self.result.setStyleSheet(style.searchResultSS())
        self.searchTextInput.setStyleSheet(style.lineEditSS())

        self.searchResultHighlighter = searchResultHighlighter()

        self.noResultsLabel = QLabel(_translate("Search", "No results found"), self.result)
        self.noResultsLabel.setVisible(False)
        self.noResultsLabel.setStyleSheet("QLabel {color: gray;}")

        # Add shortcuts for navigating through search results
        QShortcut(QKeySequence(_translate("MainWindow", "F3")), self.searchTextInput, self.nextSearchResult)
        QShortcut(QKeySequence(_translate("MainWindow", "Shift+F3")), self.searchTextInput, self.previousSearchResult)

        # These texts are already included in translation files but including ":" at the end. We force here the
        # translation for them without ":"
        _translate("MainWindow", "Situation")
        _translate("MainWindow", "Status")

    def nextSearchResult(self):
        if self.result.currentRow() < self.result.count() - 1:
            self.result.setCurrentRow(self.result.currentRow() + 1)
        else:
            self.result.setCurrentRow(0)

        if 0 < self.result.currentRow() < self.result.count():
            self.openItem(self.result.currentItem())

    def previousSearchResult(self):
        if self.result.currentRow() > 0:
            self.result.setCurrentRow(self.result.currentRow() - 1)
        else:
            self.result.setCurrentRow(self.result.count() - 1)

        if 0 < self.result.currentRow() < self.result.count():
            self.openItem(self.result.currentItem())

    def onSearchMenuChange(self):
        search_string = self.searchTextInput.text()
        self.updateSearchFeedback(search_string)

    def updateSearchFeedback(self, search_string):
        palette = QPalette()
        try:
            self.compileRegex(search_string)
        except Exception as e:
            # From https://stackoverflow.com/questions/27432456/python-qlineedit-text-color
            palette.setColor(QPalette.Text, Qt.red)

        self.searchTextInput.setPalette(palette)

    def prepareRegex(self, searchText):
        rtn = None
        try:
            rtn = self.compileRegex(searchText)
        except re.error as e:
            LOGGER.info("Problem preparing regular expression: " + e.msg)
            rtn = None
        except Exception as e:
            LOGGER.info("Problem preparing regular expression")
            rtn = None
        return rtn

    def compileRegex(self, searchText):
        # Intentionally throws exceptions for use elsewhere
        flags = re.UNICODE

        if self.searchMenu.caseSensitive() is False:
            flags |= re.IGNORECASE

        if self.searchMenu.regex() is False:
            searchText = re.escape(searchText)

        if self.searchMenu.matchWords() is True:
            # Source: https://stackoverflow.com/a/15863102
            searchText = r'\b' + searchText + r'\b'

        return re.compile(searchText, flags)

    def search(self):
        self.result.clear()
        self.result.setCurrentRow(0)

        searchText = self.searchTextInput.text()
        if len(searchText) > 0:
            results = list()
            searchRegex = self.prepareRegex(searchText)
            if searchRegex is not None:
                # Set override cursor
                qApp.setOverrideCursor(Qt.WaitCursor)

                for model, modelName in [
                    (mainWindow().mdlOutline, Model.Outline),
                    (mainWindow().mdlCharacter, Model.Character),
                    (flatDataModelWrapper(mainWindow().mdlFlatData), Model.FlatData),
                    (mainWindow().mdlWorld, Model.World),
                    (mainWindow().mdlPlots, Model.Plot)
                ]:
                    filteredColumns = self.searchMenu.columns(modelName)

                    # Searching
                    if len(filteredColumns):
                        results += model.searchOccurrences(searchRegex, filteredColumns)

                # Showing results
                self.generateResultsLists(results)

                # Remove override cursor
                qApp.restoreOverrideCursor()
            else:
                # No results to generate if there is a problem with the regex
                self.generateResultsLists(list())

    def generateResultsLists(self, results):
        self.noResultsLabel.setVisible(len(results) == 0)
        for result in results:
            item = QListWidgetItem(result.title(), self.result)
            item.setData(Qt.UserRole, result)
            item.setData(Qt.UserRole + 1, ' > '.join(result.path()))
            item.setData(Qt.UserRole + 2, result.context())
            self.result.addItem(item)

    def openItem(self, item):
        self.searchResultHighlighter.highlightSearchResult(item.data(Qt.UserRole))

    def leaveEvent(self, event):
        self.delegate.mouseLeave()


class listResultDelegate(QStyledItemDelegate):
    def __init__(self, parent=None):
        QStyledItemDelegate.__init__(self, parent)
        self._tooltipRowIndex = -1

    def paint(self, painter, option, index):
        extra = index.data(Qt.UserRole + 1)

        if not extra:
            return QStyledItemDelegate.paint(self, painter, option, index)
        else:
            if option.state & QStyle.State_Selected:
                painter.fillRect(option.rect, option.palette.color(QPalette.Highlight))

            title = index.data()
            painter.drawText(option.rect.adjusted(2, 1, 0, 0), Qt.AlignLeft, title)

            fm = QFontMetrics(option.font)
            w = fm.width(title)
            r = QRect(option.rect)
            r.setLeft(r.left() + w)
            painter.save()
            if option.state & QStyle.State_Selected:
                painter.setPen(Qt.white)
            else:
                painter.setPen(Qt.gray)
            painter.drawText(r.adjusted(2, 1, 0, 0), Qt.AlignLeft, " - {}".format(extra))
            painter.restore()

    def editorEvent(self, event, model, option, index):
        if event.type() == QEvent.MouseMove and self._tooltipRowIndex != index.row():
            self._tooltipRowIndex = index.row()
            context = index.data(Qt.UserRole + 2)
            extra = index.data(Qt.UserRole + 1)
            QToolTip.showText(event.globalPos(),
                              "<p>#" + str(index.row()) + " - " + extra + "</p><p>" + context + "</p>")
            return True
        return False

    def mouseLeave(self):
        self._tooltipRowIndex = -1
