# Window.py -- Pamhyr
# 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 os
import logging

from tools import trace, timer, logger_exception

from View.Tools.PamhyrWindow import PamhyrWindow

from PyQt5.QtGui import (
    QKeySequence, QIcon,
)

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

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

from Modules import Modules

from View.InitialConditionsAdisTS.UndoCommand import (
    SetCommand,
)

from View.InitialConditionsAdisTS.TableDefault import (
    InitialConditionTableDefaultModel,
)

from View.InitialConditionsAdisTS.Table import (
    InitialConditionTableModel, ComboBoxDelegate,
)

from View.InitialConditionsAdisTS.translate import IcAdisTSTranslate

from Solver.Mage import Mage8

_translate = QCoreApplication.translate

logger = logging.getLogger()


class InitialConditionsAdisTSWindow(PamhyrWindow):
    _pamhyr_ui = "InitialConditionsAdisTS"
    _pamhyr_name = "Initial condition AdisTS"

    def __init__(self, data=None, pollutant_id=None, study=None,
                 config=None, parent=None):
        self._data = []
        self._data.append(data)
        self._pollutant_id = pollutant_id
        trad = IcAdisTSTranslate()

        self._pollutant_name = next(
            (x.name for x in study._river._Pollutants.Pollutants_List
             if x.id == self._pollutant_id),
            None
        )
        name = (
            trad[self._pamhyr_name] +
            " - " + study.name +
            " - " + self._pollutant_name
        )

        super(InitialConditionsAdisTSWindow, self).__init__(
            title=name,
            study=study,
            config=config,
            trad=trad,
            parent=parent
        )

        self._hash_data.append(data)

        self._ics_adists_lst = study.river.ic_adists

        self.setup_table()

    def setup_table(self):

        path_icons = os.path.join(self._get_ui_directory(), f"ressources")

        table_default = self.find(QTableView, f"tableView")

        if self._study.is_editable():
            editable_headers = self._trad.get_dict("table_headers")
        else:
            editable_headers = []

        self._table = InitialConditionTableDefaultModel(
            table_view=table_default,
            table_headers=self._trad.get_dict("table_headers"),
            editable_headers=editable_headers,
            delegates={},
            data=self._data,
            undo=self._undo_stack,
            trad=self._trad
        )

        table_default.setModel(self._table)
        table_default.setSelectionBehavior(QAbstractItemView.SelectRows)
        table_default.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch
        )
        table_default.setAlternatingRowColors(True)

        layout = self.find(QVBoxLayout, f"verticalLayout_1")
        toolBar = QToolBar()
        layout.addWidget(toolBar)

        action_add = QAction(self)
        action_add.setIcon(QIcon(os.path.join(path_icons, f"add.png")))

        if self._study.is_editable():
            action_add.triggered.connect(self.add)
        action_delete = QAction(self)
        action_delete.setIcon(QIcon(os.path.join(path_icons, f"del.png")))

        if self._study.is_editable():
            action_delete.triggered.connect(self.delete)

        toolBar.addAction(action_add)
        toolBar.addAction(action_delete)

        self.table_spec = QTableView()
        layout.addWidget(self.table_spec)

        self._delegate_reach = ComboBoxDelegate(
            trad=self._trad,
            data=self._study.river,
            ic_spec_lst=self._data[0]._data,
            parent=self,
            mode="reaches"
        )
        self._delegate_rk = ComboBoxDelegate(
            trad=self._trad,
            data=self._study.river,
            ic_spec_lst=self._data[0]._data,
            parent=self,
            mode="rk"
        )

        if self._study.is_editable():
            editable_headers_spec = self._trad.get_dict("table_headers_spec")
        else:
            editable_headers_spec = []

        self._table_spec = InitialConditionTableModel(
            table_view=self.table_spec,
            table_headers=self._trad.get_dict("table_headers_spec"),
            editable_headers=editable_headers_spec,
            delegates={
                "reach": self._delegate_reach,
                "start_rk": self._delegate_rk,
                "end_rk": self._delegate_rk
            },
            data=self._data[0],
            undo=self._undo_stack,
            trad=self._trad,
            river=self._study.river
        )

        self.table_spec.setModel(self._table_spec)
        self.table_spec.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table_spec.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch
        )
        self.table_spec.setAlternatingRowColors(True)

        selectionModel = self.table_spec.selectionModel()
        index = self.table_spec.model().index(0, 0)

        selectionModel.select(
            index,
            QItemSelectionModel.Rows |
            QItemSelectionModel.ClearAndSelect |
            QItemSelectionModel.Select
        )
        self.table_spec.scrollTo(index)

    def index_selected_row(self):
        # table = self.find(QTableView, f"tableView")
        table = self.table_spec
        rows = table.selectionModel()\
                    .selectedRows()

        if len(rows) == 0:
            return 0

        return rows[0].row()

    def index_selected_rows(self):
        # table = self.find(QTableView, f"tableView")
        table = self.table_spec
        return list(
            # Delete duplicate
            set(
                map(
                    lambda i: i.row(),
                    table.selectedIndexes()
                )
            )
        )

    def move_up(self):
        row = self.index_selected_row()
        self._table.move_up(row)
        self._update()

    def move_down(self):
        row = self.index_selected_row()
        self._table.move_down(row)
        self._update()

    def _copy(self):
        rows = list(
            map(
                lambda row: row.row(),
                self.tableView.selectionModel().selectedRows()
            )
        )

        table = list(
            map(
                lambda eic: list(
                    map(
                        lambda k: eic[1][k],
                        ["rk", "discharge", "elevation"]
                    )
                ),
                filter(
                    lambda eic: eic[0] in rows,
                    enumerate(self._ics.lst())
                )
            )
        )

        self.copyTableIntoClipboard(table)

    def _paste(self):
        header, data = self.parseClipboardTable()

        if len(data) + len(header) == 0:
            return

        logger.debug(
            "IC: Paste: " +
            f"header = {header}, " +
            f"data = {data}"
        )

        try:
            row = self.index_selected_row()
            # self._table.paste(row, header, data)
            self._table.paste(row, [], data)
        except Exception as e:
            logger_exception(e)

        self._update()

    def _undo(self):
        undo_stack = self._undo_stack
        if undo_stack is None or not undo_stack.canUndo():
            return

        if isinstance(undo_stack.command(undo_stack.index() - 1), SetCommand):
            self._table.undo()
        else:
            self._table_spec.undo()

        self._update()

    def _redo(self):
        undo_stack = self._undo_stack
        if undo_stack is None or not undo_stack.canRedo():
            return

        if isinstance(undo_stack.command(undo_stack.index()), SetCommand):
            self._table.redo()
        else:
            self._table_spec.redo()

        self._update()

    def add(self):
        rows = self.index_selected_rows()
        if len(self._data[0]._data) == 0 or len(rows) == 0:
            self._table_spec.add(0)
        else:
            self._table_spec.add(rows[0])

    def delete(self):
        rows = self.index_selected_rows()
        if len(rows) == 0:
            return
        self._table_spec.delete(rows)
