# Window.py -- Pamhyr
# Copyright (C) 2023  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 pathlib
import sys
import time

from copy import deepcopy
from tools import timer, trace

from PyQt5 import QtWidgets
from PyQt5.QtGui import (
    QKeySequence,
)
from PyQt5.QtCore import (
    QModelIndex, Qt, QSettings, pyqtSlot,
    QItemSelectionModel, QCoreApplication, QSize
)
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QFileDialog, QCheckBox,
    QUndoStack, QShortcut,
)

from View.Geometry.PlotXY import PlotXY
from View.Geometry.PlotKPC import PlotKPC
from View.Geometry.PlotAC import PlotAC

from View.ASubWindow import ASubMainWindow, WindowToolKit
from View.ListedSubWindow import ListedSubWindow
from View.Geometry.mainwindow_ui_reach import Ui_MainWindow
from View.Geometry.Table import *
from View.Geometry.Profile.Window import ProfileWindow

_translate = QCoreApplication.translate


class GeometryWindow(ASubMainWindow, ListedSubWindow):
    def __init__(self, model=None, title="Geometry", parent=None):
        self._title = title
        self.parent = parent
        super(GeometryWindow, self).__init__(
            name=self._title,
            parent=parent
        )

        self._model = model
        self._reach = model.river.current_reach().reach

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.tableView = self.ui.tableView
        self.tableView_header = self.ui.tableView_header

        self._tablemodel = None
        self._profile_window = []
        self._clipboard = None

        self.setup_window()
        self.setup_sc()
        self.setup_model()
        self.setup_plots()
        self.setup_connections()
        self.changed_slider_value()

    def setup_window(self):
        self._title = f"{self.ui.mainwindow_title} - {self._reach.name}"
        self.setWindowTitle(self._title)

    def setup_sc(self):
        self._undo_stack = QUndoStack()

        self.undo_sc = QShortcut(QKeySequence.Undo, self)
        self.redo_sc = QShortcut(QKeySequence.Redo, self)
        self.copy_sc = QShortcut(QKeySequence.Copy, self)
        self.paste_sc = QShortcut(QKeySequence.Paste, self)

    def setup_model(self):
        self._tablemodel = TableEditableModel(
            headers = self.ui.tableView_header,
            reach = self._reach,
            undo = self._undo_stack
        )
        self.tableView.setModel(self._tablemodel)
        self.tableView.setItemDelegate(Delegate())

    def setup_plots(self):
        self.plot_xy()
        self.plot_kpc()
        self.plot_ac()

    def setup_connections(self):
        self.ui.btn_open.triggered.connect(self.open_file_dialog)
        self.ui.btn_sort_asc.triggered.connect(self.sort_ascending)
        self.ui.btn_sort_desc.triggered.connect(self.sort_descending)
        self.ui.btn_move_up.triggered.connect(self.move_row_up)
        self.ui.btn_move_down.triggered.connect(self.move_row_down)
        self.ui.btn_end_editing.triggered.connect(self.handleSave)
        self.ui.btn_add.triggered.connect(self.insert_row)
        self.ui.btn_delete.triggered.connect(self.delete_rows)
        self.ui.btn_edit.triggered.connect(self.edit_profile)
        self.ui.verticalSlider.valueChanged.connect(self.changed_slider_value)

        self.ui.btn_slider_up.clicked.connect(self.decrement_value_slider)
        self.ui.btn_slider_down.clicked.connect(self.increment_value_slider)
        self.ui.btn_move_up.triggered.connect(self.changed_profile_slot)

        self.undo_sc.activated.connect(self.undo)
        self.redo_sc.activated.connect(self.redo)
        self.copy_sc.activated.connect(self.copy)
        self.paste_sc.activated.connect(self.paste)

        # Profile selection when line change in table
        self.tableView.selectionModel()\
                      .selectionChanged\
                      .connect(self.select_current_profile)

    def open_file_dialog(self):
        options = QFileDialog.Options()
        settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'MyOrg', )
        options |= QFileDialog.DontUseNativeDialog

        filename, _ = QtWidgets.QFileDialog.getOpenFileName(
            self,
            _translate("MainWindow_reach", "Ouvrir un fichier"),
            "",
            _translate("MainWindow_reach", "Fichiers .ST (*.ST)") +
            ";; " +
            _translate("MainWindow_reach", "Fichiers .M (*.M)") +
            ";; " +
            _translate("MainWindow_reach", "Tous les fichiers (*)"),
            options=options
        )

        if filename != "":
            size = os.stat(filename).st_size
            self._reach.import_geometry(filename)
            self._tablemodel.layoutChanged.emit()

            self.update_profile_windows()
            self.plot_xy()
            self.plot_kpc()
            self.plot_ac()

    def messagebox_profile_editing(self):
        msg_box = QtWidgets.QMessageBox()
        msg_box.setIcon(QtWidgets.QMessageBox.Information)
        msg_box.setWindowTitle(_translate("MainWindow_reach",
                                          "Édition des profils sélectionnés"))
        msg_box.setText(_translate("MainWindow_reach",
                                   "Vous avez sélectionné plus de 5 profils."
                                   " \nSeuls les 5 premiers seront édités."))
        msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok)

        return_value = msg_box.exec()
        # if return_value == QtWidgets.QMessageBox.Ok:
        #     print('OK clicked')

    def edit_profile(self):
        self.tableView.model().blockSignals(True)

        rows = list(
            set(
                (i.row() for i in self.tableView.selectedIndexes())
            )
        )

        for row in rows:
            profile = self._reach.profile(row)

            win = ProfileWindow(
                profile = profile,
                parent = self,
            )

            self._profile_window.append(win)
            win.show()

        self.tableView.model().blockSignals(False)

    pyqtSlot(bool)

    def changed_profile_slot(self, status):
        self.update_view1 = status

    def update_profile_windows(self):
        self.list_second_window = []
        self.list_row = []

    def plot_xy(self):
        self.tableView.model().blockSignals(True)

        self._plot_xy = PlotXY(
            canvas = self.ui.canvas_1,
            data = self._reach,
            toolbar = self.ui.toolbar_1
        )
        self._plot_xy.draw()

        self.tableView.model().blockSignals(False)

    def update_plot_xy(self):
        self.tableView.model().blockSignals(True)
        self._plot_xy.update()
        self.tableView.model().blockSignals(False)

    def plot_kpc(self):
        self.tableView.model().blockSignals(True)

        self._plot_kpc = PlotKPC(
            canvas = self.ui.canvas_2,
            data = self._reach,
            toolbar = self.ui.toolbar_2
        )
        self._plot_kpc.draw()

        self.tableView.model().blockSignals(False)

    def update_plot_kpc(self):
        self.tableView.model().blockSignals(True)
        self._plot_kpc.update()
        self.tableView.model().blockSignals(False)

    def plot_ac(self):
        self.tableView.model().blockSignals(True)

        self._plot_ac = PlotAC(
            canvas = self.ui.canvas_3,
            data = self._reach,
            toolbar = self.ui.toolbar_3,
            plot_xy = self._plot_xy
        )
        self._plot_ac.draw()

        self.tableView.model().blockSignals(False)

    def update_plot_ac(self, ind: int):
        self.tableView.model().blockSignals(True)
        self._plot_ac.update(ind=ind)
        self.tableView.model().blockSignals(False)

    def get_station(self, ind: int):
        return self._reach.profile(ind).get_station()

    def get_elevation(self, ind: int):
        return self._reach.profile(ind).z()

    def select_plot_xy(self, ind: int):
        self.tableView.model().blockSignals(True)
        self._plot_xy.update(ind=ind)
        self.tableView.model().blockSignals(False)

    def select_plot_kpc(self, ind: int):
        self.tableView.model().blockSignals(True)
        self._plot_kpc.update(ind=ind)
        self.tableView.model().blockSignals(False)

    def select_plot_ac(self, ind: int):
        self.tableView.model().blockSignals(True)
        self._plot_ac.update(ind=ind)
        self.tableView.model().blockSignals(False)

    def select_row_profile_slider(self, ind: int = 0):
        if self.tableView is not None:
            selectionModel = self.tableView.selectionModel()
            index = self.tableView.model().index(ind, 0)

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

            self.tableView.scrollTo(index)

    def select_current_profile(self):
        self.tableView.model().blockSignals(True)

        if len(self.tableView.selectedIndexes()) > 0:
            row = self.index_selected_row()

            self.ui.verticalSlider.setValue(row)
            self.select_plot_xy(row)
            self.select_plot_kpc(row)
            self.select_plot_ac(row)

        self.tableView.model().blockSignals(False)

    def changed_slider_value(self):
        self.tableView.model().blockSignals(True)

        if self._tablemodel.rowCount() != 0:
            self.ui.verticalSlider.setMaximum(self._tablemodel.rowCount() - 1)

            slider_value = self.ui.verticalSlider.value()
            kp = self._reach.profile(slider_value).kp

            self.ui.vertical_slider_label.setText(
                _translate("MainWindow_reach", "Kp : ") +
                f"{kp}" + "\n" +
                _translate("MainWindow_reach",
                           "Profil N° : ") +
                f"{slider_value + 1}"
            )

            self.select_plot_xy(slider_value)
            self.select_plot_kpc(slider_value)
            self.select_row_profile_slider(slider_value)

        self.tableView.model().blockSignals(False)

    def increment_value_slider(self):
        if 0 <= self.ui.verticalSlider.value() < self._tablemodel.rowCount() - 1:
            self.ui.verticalSlider.setValue(self.ui.verticalSlider.value() + 1)

    def decrement_value_slider(self):
        if 0 < self.ui.verticalSlider.value() < self._tablemodel.rowCount():
            self.ui.verticalSlider.setValue(self.ui.verticalSlider.value() - 1)

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

    def delete_rows(self):
        rows = sorted(
            list(
                set(
                    [index.row() for index in self.tableView.selectedIndexes()]
                )
            )
        )

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

        self.update_plot_xy()
        self.select_current_profile()

        self.plot_kpc()
        self.changed_slider_value()

    def index_selected_row(self):
        return self.tableView\
                   .selectionModel()\
                   .selectedRows()[0]\
                   .row()

    def sort_ascending(self):
        self._tablemodel.sort_profiles(True)
        self.select_current_profile()
        self.changed_slider_value()

    def sort_descending(self):
        self._tablemodel.sort_profiles(False)

        self.select_current_profile()
        self.changed_slider_value()

    def move_row_up(self):
        row = self.index_selected_row()
        self._tablemodel.move_row_up(row)
        self.select_current_profile()

    def move_row_down(self):
        row = self.index_selected_row()
        self._tablemodel.move_row_down(row)
        self.select_current_profile()

    def duplicate(self):
        rows = [
            row.row() for row in
            self.tableView.selectionModel().selectedRows()
        ]

        profiles = []
        for row in rows:
            profiles.append(
                self._reach.profile(row)
            )

        if len(profiles) == 0:
            return

        self._tablemodel.duplicate(rows, profiles)
        self.select_current_profile()

    def copy(self):
        rows = self.tableView\
                   .selectionModel()\
                   .selectedRows()

        table = []
        table.append(["name", "kp"])

        for row in rows:
            profile = self._reach.profile(row.row())
            table.append(
                [profile.name, profile.kp]
            )

        self.copyTableIntoClipboard(table)

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

        if len(data) == 0:
            return

        if len(header) != 0:
            header.append("reach")
        for row in data:
            row.append(self._reach)

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

    def undo(self):
        self._tablemodel.undo()
        self.select_current_profile()
        self.update_plot_xy()
        self.update_plot_kpc()

    def redo(self):
        self._tablemodel.redo()
        self.select_current_profile()
        self.update_plot_xy()
        self.update_plot_kpc()

    def handleSave(self):
        options = QFileDialog.Options()
        DEFAULT_DIRECTORY = '/home/'
        settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'MyOrg', )
        current_dir = settings.value('current_directory', DEFAULT_DIRECTORY, type=str)
        options |= QFileDialog.DontUseNativeDialog

        filename, filters = QFileDialog.getSaveFileName(
            self,
            filter=_translate("MainWindow_reach",
                              "Files .ST(*.ST or *.st)")
            + ";; " +
            _translate("MainWindow_reach", "All files "
                       "(*)"),
            options=options
        )

        current_dir = os.path.split(filename)[0] or DEFAULT_DIRECTORY

        if filename != '':
            self._tablemodel.export_reach(filename)

    def handleOpen(self):
        filename, filterName = QFileDialog.getOpenFileName(self)

        if filename != '':
            with open(filename, 'r') as f:
                reader = csv.reader(f, delimiter='\t')
                header = next(reader)
                buf = []
                for row in reader:
                    row[0] = QCheckBox("-")
                    buf.append(row)

                self._tablemodel = None
                self._tablemodel = TableEditableModel(buf)
                self.tableView.setModel(self._tablemodel)
                filename = ''