import re

from nmreval.lib.colors import BaseColor, Colors
from nmreval.lib.lines import LineStyle
from nmreval.lib.symbols import SymbolStyle, make_symbol_pixmap

from ..Qt import QtWidgets, QtGui, QtCore


class PropertyDelegate(QtWidgets.QStyledItemDelegate):
    def paint(self, painter: QtGui.QPainter, options: QtWidgets.QStyleOptionViewItem, idx: QtCore.QModelIndex):
        r = idx.data(QtCore.Qt.DisplayRole)
        if r is not None:
            if isinstance(r, BaseColor):
                painter.save()
                c = QtGui.QColor(*r.value)
                rect = options.rect
                painter.fillRect(rect, QtGui.QBrush(c))
                painter.restore()

            elif isinstance(r, LineStyle):
                painter.save()
                pen = QtGui.QPen()
                pal = QtGui.QGuiApplication.palette()
                pen.setColor(pal.color(QtGui.QPalette.Text))
                pen.setWidth(2)
                pen.setStyle(r.value)
                pen.setCapStyle(QtCore.Qt.RoundCap)
                painter.setPen(pen)

                rect = options.rect
                rect.adjust(5, 0, -5, 0)
                mid = (rect.bottom()+rect.top()) / 2
                painter.drawLine(rect.left(), mid, rect.right(), mid)
                painter.restore()

            elif isinstance(r, SymbolStyle):
                painter.save()
                pen = QtGui.QPen()
                pal = QtGui.QGuiApplication.palette()
                pen.setColor(pal.color(QtGui.QPalette.Text))
                painter.setPen(pen)

                pm = make_symbol_pixmap(r)
                painter.drawPixmap(options.rect.topLeft()+QtCore.QPoint(3, (options.rect.height()-pm.height())/2), pm)

                style = QtWidgets.QApplication.style()
                text_rect = style.subElementRect(QtWidgets.QStyle.SE_ItemViewItemText, options, None)
                text_rect.adjust(5+pm.width(), 0, 0, 0)
                painter.drawText(text_rect, options.displayAlignment, r.name)

                painter.restore()

            else:
                super().paint(painter, options, idx)

    def createEditor(self, parent: QtWidgets.QWidget,
                     options: QtWidgets.QStyleOptionViewItem, idx: QtCore.QModelIndex) -> QtWidgets.QWidget:
        data = idx.data()
        if isinstance(data, BaseColor):
            editor = ColorListEditor(parent)

        elif isinstance(data, LineStyle):
            editor = LineStyleEditor(parent)

        elif isinstance(data, SymbolStyle):
            editor = SymbolStyleEditor(parent)

        elif isinstance(data, float):
            editor = SpinBoxEditor(parent)

        else:
            editor = super().createEditor(parent, options, idx)

        return editor

    def setEditorData(self, editor: QtWidgets.QWidget, idx: QtCore.QModelIndex):
        data = idx.data()
        if isinstance(data, (BaseColor, LineStyle, SymbolStyle)):
            editor.value = data
        else:
            super().setEditorData(editor, idx)

    def setModelData(self, editor: QtWidgets.QWidget, model: QtCore.QAbstractItemModel, idx: QtCore.QModelIndex):
        data = idx.data()
        if isinstance(data, (BaseColor, LineStyle, SymbolStyle)):
            model.setData(idx, editor.value)
        else:
            super().setModelData(editor, model, idx)


class ColorListEditor(QtWidgets.QComboBox):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.populateList()

    @QtCore.pyqtProperty(BaseColor, user=True)
    def value(self):
        return self.itemData(self.currentIndex())

    @value.setter
    def value(self, val):
        for i in range(self.count()):
            if val.name == self.itemData(i).name:
                self.setCurrentIndex(i)
                break

    def populateList(self):
        for i, colorName in enumerate(Colors):
            color = QtGui.QColor(*colorName.value)
            self.insertItem(i, colorName.name)
            self.setItemData(i, colorName)
            px = QtGui.QPixmap(self.iconSize())
            px.fill(color)
            self.setItemData(i, QtGui.QIcon(px), QtCore.Qt.DecorationRole)


class SpinBoxEditor(QtWidgets.QDoubleSpinBox):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setValue(1.0)
        self.setSingleStep(0.1)
        self.setDecimals(1)

    @QtCore.pyqtProperty(float, user=True)
    def value(self):
        return super().value()

    @value.setter
    def value(self, val):
        super().setValue(val)


class LineStyleEditor(QtWidgets.QComboBox):
    def __init__(self, parent=None):
        super().__init__(parent=parent)

        self.setItemDelegate(LineStyleDelegate())
        self.populate()

    @QtCore.pyqtProperty(int, user=True)
    def value(self):
        return self.itemData(self.currentIndex())

    @value.setter
    def value(self, val):
        for i in range(self.count()):
            if val == self.itemData(i):
                self.setCurrentIndex(i)
                break

    def populate(self):
        for i, style in enumerate(LineStyle):
            self.insertItem(i, re.sub(r'([A-Z])', r' \g<1>', style.name))
            self.setItemData(i, style)

    def paintEvent(self, evt: QtGui.QPaintEvent):
        if self.currentData() is not None:
            painter = QtWidgets.QStylePainter(self)
            opt = QtWidgets.QStyleOptionComboBox()
            self.initStyleOption(opt)
            painter.drawComplexControl(QtWidgets.QStyle.CC_ComboBox, opt)
            pen = QtGui.QPen()
            pal = QtGui.QGuiApplication.palette()
            pen.setColor(pal.color(QtGui.QPalette.Text))
            pen.setWidth(2)
            pen.setStyle(self.currentData().value)
            pen.setCapStyle(QtCore.Qt.RoundCap)
            painter.setPen(pen)

            rect = painter.style().subControlRect(QtWidgets.QStyle.CC_ComboBox,
                                                  opt, QtWidgets.QStyle.SC_ComboBoxEditField, None)
            rect.adjust(+10, 0, -10, 0)
            mid = (rect.bottom() + rect.top()) / 2
            painter.drawLine(rect.left(), mid, rect.right(), mid)
            painter.end()
        else:
            super().paintEvent(evt)


class LineStyleDelegate(QtWidgets.QStyledItemDelegate):
    def paint(self, painter, option, index):
        data = index.data(QtCore.Qt.UserRole)

        if data is not None:
            pen = QtGui.QPen()
            pal = QtGui.QGuiApplication.palette()
            pen.setColor(pal.color(QtGui.QPalette.Text))
            pen.setWidth(2)
            pen.setStyle(data.value)
            pen.setCapStyle(QtCore.Qt.RoundCap)
            painter.setPen(pen)

            rect = option.rect
            rect.adjust(+10, 0, -10, 0)
            mid = (rect.bottom()+rect.top()) / 2
            painter.drawLine(rect.left(), mid, rect.right(), mid)
        else:
            QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)


class SymbolStyleEditor(QtWidgets.QComboBox):
    def __init__(self, parent=None):
        super().__init__(parent=parent)

        self.populate()

    @QtCore.pyqtProperty(SymbolStyle, user=True)
    def value(self):
        return self.itemData(self.currentIndex())

    @value.setter
    def value(self, val):
        for i in range(self.count()):
            if val == self.itemData(i):
                self.setCurrentIndex(i)
                break

    def populate(self):
        for i, s in enumerate(SymbolStyle):
            self.insertItem(i, re.sub(r'([A-Z])', r' \g<1>', s.name))
            self.setItemData(i, s)
            self.setItemData(i, make_symbol_pixmap(s), QtCore.Qt.DecorationRole)


class HeaderDelegate(QtWidgets.QStyledItemDelegate):
    def paint(self, painter: QtGui.QPainter, option: QtWidgets.QStyleOptionViewItem, index: QtCore.QModelIndex):

        header_option = QtWidgets.QStyleOptionHeader()
        header_option.rect = option.rect

        style = QtWidgets.QApplication.style()
        style.drawControl(QtWidgets.QStyle.CE_HeaderSection, header_option, painter)

        text_rect = style.subElementRect(QtWidgets.QStyle.SE_ItemViewItemText, option, None)
        text_rect.adjust(2, 0, 0, 0)

        if option.state & QtWidgets.QStyle.State_Selected:
            painter.fillRect(option.rect, option.palette.highlight())

        painter.drawText(text_rect, option.displayAlignment, index.data(QtCore.Qt.DisplayRole))
