# PamhyrTable.py -- Pamhyr abstract table model
# Copyright (C) 2023-2026  INRAE
#
# 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 <https://www.gnu.org/licenses/>.

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

import logging
import traceback

from tools import trace, timer

from Model.Except import NotImplementedMethodeError

from PyQt5.QtCore import (
    Qt, QVariant, QAbstractTableModel,
    QCoreApplication, QModelIndex, pyqtSlot,
    QRect,
)

from PyQt5.QtWidgets import (
    QDialogButtonBox, QPushButton, QLineEdit,
    QFileDialog, QTableView, QAbstractItemView,
    QUndoStack, QShortcut, QAction, QItemDelegate,
    QComboBox, QStyledItemDelegate, QHeaderView,
)

logger = logging.getLogger()


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

    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())

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


class PamhyrTableModel(QAbstractTableModel):
    def _setup_delegates(self):
        if self._table_view is None:
            return

        for h in self._headers:
            if h in self._delegates:
                self._table_view.setItemDelegateForColumn(
                    self._headers.index(h), self._delegates[h]
                )
            else:
                self._table_view.setItemDelegateForColumn(
                    self._headers.index(h), PamhyrTextDelegate(
                        parent=self
                    )
                )

    def __init__(self,
                 table_view=None,
                 table_headers={},
                 editable_headers=[],
                 delegates={},
                 trad=None,
                 data=None,
                 undo=None,
                 opt_data=None,
                 options=["rows_selection"],
                 parent=None):
        super(PamhyrTableModel, self).__init__()

        self._table_view = table_view

        self._table_headers = table_headers
        self._headers = list(table_headers.keys())
        self._editable_headers = editable_headers
        self._delegates = delegates
        self._trad = trad
        self._parent = parent

        self._data = data
        self._opt_data = opt_data
        self._options = options
        self._undo = undo
        self._lst = []

        self._setup_delegates()
        self._setup_lst()
        self._table_view_configure()

    def _setup_lst(self):
        self._lst = self._data

    def _table_view_configure(self):
        self._table_view.setModel(self)
        if "rows_selection" in self._options:
            self._table_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self._table_view.horizontalHeader()\
                        .setSectionResizeMode(QHeaderView.Stretch)
        self._table_view.setAlternatingRowColors(True)
        self._table_view.resizeColumnsToContents()

    def flags(self, index):
        column = index.column()

        options = Qt.ItemIsEnabled | Qt.ItemIsSelectable

        if self._headers[column] in self._editable_headers:
            options |= Qt.ItemIsEditable

        return options

    def rowCount(self, parent=QModelIndex()):
        return len(self._lst)

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

    def headerData(self, section, orientation, role):
        if role == Qt.ItemDataRole.DisplayRole:
            if orientation == Qt.Orientation.Horizontal:
                return self._table_headers[self._headers[section]]

        return QVariant()

    def is_same_data(self, index, value):
        current = self.data(index, Qt.ItemDataRole.DisplayRole)
        if isinstance(current, QVariant):
            current = current.value()

        if str(current) == str(value):
            return True

        if hasattr(value, "display_name"):
            if str(current) == str(value.display_name()):
                return True

        if hasattr(value, "name"):
            if str(current) == str(value.name):
                return True

        try:
            return (
                float(str(current).replace(",", ".")) ==
                float(str(value).replace(",", "."))
            )
        except (TypeError, ValueError):
            return False

    def data(self, index, role):
        raise NotImplementedMethodeError(self, self.data)

    def setData(self, index, value, role=Qt.EditRole):
        raise NotImplementedMethodeError(self, self.setData)

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

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

    def update(self):
        self.layoutChanged.emit()
