# -*- coding: utf-8 -*-

import numpy as np

from tools import timer, trace

from PyQt5.QtGui import (
    QFont, QColor
)
from PyQt5.QtWidgets import (
    QMessageBox, QStyledItemDelegate, QLineEdit
)
from PyQt5.QtCore import (
    QModelIndex, Qt, QAbstractTableModel,
    QVariant, QCoreApplication
)

from Model.Geometry.PointXYZ import PointXYZ
from Model.Geometry.ProfileXYZ import ProfileXYZ

from View.Geometry.Profile.UndoCommand import *

_translate = QCoreApplication.translate


class TableEditableModel(QAbstractTableModel):
    def __init__(self, profile: ProfileXYZ, header=None, undo=None):
        QAbstractTableModel.__init__(self)

        self._undo_stack = undo
        self._profile = profile

        if header is None:
            self._header = [
                "X (m)", "Y (m)", "Z (m)",
                _translate("MainWindowProfile", "Nom"),
                _translate("MainWindowProfile", "Abs en travers (m)")
            ]
        else:
            self._header = header

    def rowCount(self, parent=QModelIndex()):
        return self._profile.number_points

    def columnCount(self, parent=QModelIndex()):
        return len(self._header)

    def data(self, index, role=Qt.DisplayRole):
        if index.isValid():
            if role == Qt.DisplayRole:
                value = ""

                if index.column() == 0:
                    value = self._profile.point(index.row()).x
                elif index.column() == 1:
                    value = self._profile.point(index.row()).y
                elif index.column() == 2:
                    value = self._profile.point(index.row()).z
                elif index.column() == 3:
                    value = self._profile.point(index.row()).name
                elif index.column() == 4:
                    station = self._profile.get_station()
                    if station is None:
                        return "-"
                    else:
                        value = station[index.row()]
                        return f"{value:.3f}"

                if 0 <= index.column() < 3:
                    return f"{value:.4f}"

                return f"{value}"

            if role == Qt.TextAlignmentRole:
                return Qt.AlignHCenter | Qt.AlignVCenter

            if index.column() == 2:
                value = self._profile.point(index.row()).z
                if role == Qt.ForegroundRole:
                    if value == self._profile.z_min():
                        return QColor("red")
                    elif value == self._profile.z_max():
                        return QColor("blue")

                if role == Qt.ToolTipRole:
                    if value == self._profile.z_min():
                        return _translate("MainWindowProfile",
                                          "La cote du fond",
                                          "Z minimale")
                    elif value == self._profile.z_max():
                        return _translate("MainWindowProfile",
                                          "La cote maximale",
                                          "Z maximale")

            if index.column() == 3:
                value = self._profile.point(index.row()).name

                if value.strip().upper() in ["RG", "RD"]:
                    if role == Qt.FontRole:
                        font = QFont()
                        font.setBold(True)
                        return font

                    if role == Qt.ForegroundRole:
                        return QColor("darkRed")

                    if role == Qt.ToolTipRole:
                        if value.strip().upper() == "RG":
                            return _translate("MainWindowProfile", "Rive gauche")
                        else:
                            return _translate("MainWindowProfile", "Rive droite")

        return QVariant()

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self._header[section]
        elif orientation == Qt.Vertical and role == Qt.DisplayRole:
            return str(section + 1)

        if role == Qt.ToolTipRole and section == 4:
            return _translate(
                "MainWindowProfile",
                "Abscisse en travers calculée en projétant les points"
                " \nsur le plan défini par les deux points nommés extrêmes "
            )

        return QVariant()

    def setData(self, index, value, role=Qt.EditRole):
        row = index.row()
        column = index.column()

        if role == Qt.EditRole:
            if column == 0:
                self._undo_stack.push(
                    SetXCommand(
                        self._profile, row,
                        self._profile.point(row).x,
                        value
                    )
                )
            elif column == 1:
                self._undo_stack.push(
                    SetYCommand(
                        self._profile, row,
                        self._profile.point(row).y,
                        value
                    )
                )
            elif column == 2:
                self._undo_stack.push(
                    SetZCommand(
                        self._profile, row,
                        self._profile.point(row).z,
                        value
                    )
                )
            elif column == 3:
                self._undo_stack.push(
                    SetNameCommand(
                        self._profile, row,
                        self._profile.point(row).name,
                        value
                    )
                )

            self.dataChanged.emit(index, index)
            return True

        self.dataChanged.emit(index, index)
        self.layoutChanged.emit()
        return False

    def index(self, row, column, parent=QModelIndex()):
        if not self.hasIndex(row, column, parent):
            return QModelIndex()

        return self.createIndex(row, column, QModelIndex())

    def flags(self, index):
        flg = Qt.ItemIsSelectable

        if index.column() == 4:
            return flg

        return Qt.ItemIsEditable | Qt.ItemIsEnabled | flg

    def insert_row(self, row, parent=QModelIndex()):
        self.beginInsertRows(parent, row, row - 1)

        self._undo_stack.push(
            AddCommand(
                self._profile, row
            )
        )

        self.endInsertRows()
        self.layoutChanged.emit()

    def remove_rows(self, rows, parent=QModelIndex()):
        self.beginRemoveRows(parent, rows[0], rows[-1])

        self._undo_stack.push(
            DelCommand(
                self._profile, rows
            )
        )

        self.endRemoveRows()
        self.layoutChanged.emit()

    def sort(self, column='x', order=Qt.AscendingOrder):
        self.layoutAboutToBeChanged.emit()

        reverse = (order != Qt.AscendingOrder)

        self._undo_stack.push(
            SortCommand(
                self._profile, column, reverse
            )
        )

        self.layoutChanged.emit()

    def move_row_up(self, row, parent=QModelIndex()):
        if row <= 0:
            return

        target = row + 2

        self.beginMoveRows(parent, row - 1, row - 1, parent, target)

        self._undo_stack.push(
            MoveCommand(
                self._profile, "up", row
            )
        )

        self.endMoveRows()
        self.layoutChanged.emit()

    def move_row_down(self, row_to_move, parent=QModelIndex()):
        if row > self._profile.number_points:
            return

        target = row

        self.beginMoveRows(parent, row + 1, row + 1, parent, target)

        self._undo_stack.push(
            MoveCommand(
                self._profile, "down", row
            )
        )

        self.endMoveRows()
        self.layoutChanged.emit()

    def paste(self, row, header, data):
        if row > self._profile.number_points:
            return

        if len(data) == 0:
            return

        self.layoutAboutToBeChanged.emit()

        self._undo_stack.push(
            PasteCommand(
                self._profile, row,
                list(
                    map(
                        lambda d: PointXYZ.from_data(header, d),
                        data
                    )
                )
            )
        )

        self.layoutAboutToBeChanged.emit()
        self.layoutChanged.emit()

    def undo(self):
        self._undo_stack.undo()
        self.layoutChanged.emit()

    def redo(self):
        self._undo_stack.redo()
        self.layoutChanged.emit()


class Delegate(QStyledItemDelegate):
    def __init__(self, parent=None, setModelDataEvent=None):
        super(Delegate, self).__init__(parent)
        self.setModelDataEvent = setModelDataEvent

    def createEditor(self, parent, option, index):
        index.model().data(index, Qt.DisplayRole)
        return QLineEdit(parent)

    def setEditorData(self, editor, index):
        value = index.model().data(index, Qt.DisplayRole)
        editor.setText(str(value))

    def setModelData(self, editor, model, index):
        model.setData(index, editor.text())

        if not self.setModelDataEvent is None:
            self.setModelDataEvent()

    def updateEditorGeometry(self, editor, option, index):
        editor.setGeometry(option.rect)