# 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 copy
import sys
import csv
from time import time
import logging

from tools import trace, timer, logger_exception
from Modules import Modules

from PyQt5.QtGui import (
    QKeySequence,
)
from PyQt5.QtCore import (
    QModelIndex, Qt, QEvent, QCoreApplication
)
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QFileDialog, QCheckBox,
    QUndoStack, QShortcut, QTableView, QAbstractItemView,
    QHeaderView, QVBoxLayout, QAction,
)

from Model.Geometry.Reach import Reach
from Model.Geometry.ProfileXYZ import ProfileXYZ

from View.Tools.PamhyrWindow import PamhyrWindow
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.Tools.Plot.PamhyrCanvas import MplCanvas

from View.Geometry.Profile.Plot import Plot
from View.Geometry.Profile.Table import GeometryProfileTableModel
from View.Geometry.Profile.Translate import GeometryProfileTranslate
from View.Geometry.PurgeDialog import PurgeDialog

_translate = QCoreApplication.translate

logger = logging.getLogger()


class ProfileWindow(PamhyrWindow):
    _pamhyr_ui = "GeometryCrossSection"
    _pamhyr_name = "Geometry cross-section"

    def __init__(self, profile=None, study=None, config=None, parent=None):
        self._profile = profile

        trad = GeometryProfileTranslate()
        name = (
            trad[self._pamhyr_name] +
            f" - {self._profile.name} {self._profile.rk}"
        )

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

        self._hash_data.append(profile)

        self.setup_table()
        self.setup_plot()
        self.setup_connections()

    def setup_table(self):
        if self._study.is_read_only():
            editable_headers = []
        else:
            editable_headers = ["name", "x", "y", "z"]

        table_headers = self._trad.get_dict("table_headers")

        table = self.find(QTableView, "tableView")
        self._tablemodel = GeometryProfileTableModel(
            table_view=table,
            table_headers=table_headers,
            editable_headers=editable_headers,
            data=self._profile,
            undo=self._undo_stack
        )
        table.setModel(self._tablemodel)
        table.setSelectionBehavior(QAbstractItemView.SelectRows)
        table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        table.setAlternatingRowColors(True)

    def setup_plot(self):
        self._tablemodel.blockSignals(True)

        self._canvas = MplCanvas(width=3, height=4, dpi=100)
        self._canvas.setObjectName("canvas")
        self._toolbar = PamhyrPlotToolbar(
            self._canvas, self,
            items=["home", "zoom", "save", "iso", "back/forward", "move"]
        )
        self._plot_layout = self.find(QVBoxLayout, "verticalLayout")
        self._plot_layout.addWidget(self._toolbar)
        self._plot_layout.addWidget(self._canvas)

        self._plot = Plot(
            canvas=self._canvas,
            data=self._profile,
            trad=self._trad,
            toolbar=self._toolbar,
            table=self.find(QTableView, "tableView"),
            parent=self
        )
        self._plot.draw()

        self._tablemodel.blockSignals(False)

    def setup_connections(self):
        if self._study.is_read_only():
            actions = {}
        else:
            actions = {
                "action_sort_asc": self.sort_Y_ascending,
                "action_sort_des": self.sort_Y_descending,
                "action_up": self.move_up,
                "action_down": self.move_down,
                "action_add": self.add,
                "action_delete": self.delete,
                "action_purge": self.purge,
                "action_reverse": self.reverse,
            }

        for action in actions:
            self.find(QAction, action)\
                .triggered.connect(actions[action])

        table = self.find(QTableView, f"tableView")
        table.selectionModel()\
             .selectionChanged\
             .connect(self.update_points_selection)

        self._tablemodel.dataChanged.connect(self.update)

    def update_points_selection(self):
        rows = self.index_selected_rows()
        self._plot.select_points_from_indices(rows)

    def update(self):
        self.update_plot()
        self._propagate_update(key=Modules.GEOMETRY)

    def _update(self, redraw=False, propagate=True):
        if redraw:
            self.update_plot()
        if propagate:
            self._propagate_update(key=Modules.GEOMETRY)

    def update_plot(self):
        self._tablemodel.blockSignals(True)

        self._plot.update()

        self._tablemodel.blockSignals(False)

    def _propagated_update(self, key=Modules(0)):
        if Modules.GEOMETRY not in key:
            return

        self._tablemodel.layoutChanged.emit()
        self._update(redraw=True, propagate=False)

    def index_selected_row(self):
        table = self.find(QTableView, "tableView")
        rows = table.selectionModel()\
                    .selectedRows()
        if len(rows) == 0:
            return 0

        return rows[0].row()

    def index_selected_rows(self):
        table = self.find(QTableView, "tableView")
        return list(
            map(
                lambda r: r.row(),
                table.selectionModel().selectedRows()
            )
        )

    def add(self):
        table = self.find(QTableView, "tableView")

        if len(table.selectedIndexes()) == 0:
            self._tablemodel.insert_row(self._tablemodel.rowCount())
        else:
            row = self.index_selected_row()
            self._tablemodel.insert_row(row + 1)

        self.update()

    def delete(self):
        table = self.find(QTableView, "tableView")
        rows = sorted(
            list(
                set(
                    [index.row() for index in table.selectedIndexes()]
                )
            )
        )

        if len(rows) > 0:
            self._tablemodel.remove_rows(rows)
            self.update()

    def sort_X_ascending(self):
        self._tablemodel.sort('x', order=Qt.AscendingOrder)
        self.update()

    def sort_X_descending(self):
        self._tablemodel.sort('x', order=Qt.DescendingOrder)
        self.update()

    def sort_Y_ascending(self):
        self._tablemodel.sort('y', order=Qt.AscendingOrder)
        self.update()

    def sort_Y_descending(self):
        self._tablemodel.sort('y', order=Qt.DescendingOrder)
        self.update()

    def move_down(self):
        rows = list(
            set(
                [index.row() for index in
                 self.find(QTableView, "tableView").selectedIndexes()]
            )
        )

        for row in rows:
            if row < self._tablemodel.rowCount() - 1:
                self._tablemodel.move_down(row)

        self.update()

    def move_up(self):
        rows = list(
            set(
                [index.row() for index in
                 self.find(QTableView, "tableView").selectedIndexes()]
            )
        )

        for row in rows:
            if 0 < row:
                self._tablemodel.move_up(row)

        self.update()

    def purge(self):
        self._tablemodel.purge()
        self.update()

    def purge(self):
        try:
            dlg = PurgeDialog(
                trad=self._trad,
                parent=self
            )
            if dlg.exec():
                self._tablemodel.purge(dlg.np_purge)
                self._plot.draw()
        except Exception as e:
            logger_exception(e)
            return

    def reverse(self):
        self._tablemodel.reverse()
        self.update()

    def _copy(self):
        table = self.find(QTableView, "tableView")
        rows = table.selectionModel().selectedRows()

        data = []
        data.append(["name", "x", "y", "z"])

        for row in rows:
            point = self._profile.point(row.row())
            data.append(
                [
                    point.name, point.x, point.y, point.z
                ]
            )

        self.copyTableIntoClipboard(data)

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

        if len(data) == 0:
            return

        if len(header) != 0:
            header.append("profile")
        # for row in data:
        #     row.append(self._profile)

        row = self.index_selected_row()
        self._tablemodel.paste(row, header, data)
        self.update()

    def _undo(self):
        self._tablemodel.undo()
        self.update()

    def _redo(self):
        self._tablemodel.redo()
        self.update()
