# PlotRKC.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 logging

from tools import timer
from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import (
    Qt, QItemSelectionModel,
    QItemSelection, QItemSelectionRange,
)

logger = logging.getLogger()


class PlotRKZ(PamhyrPlot):
    def __init__(self, canvas=None, trad=None,
                 study=None, data=None, toolbar=None,
                 table=None, parent=None):
        self._study = study

        super(PlotRKZ, self).__init__(
            canvas=canvas,
            trad=trad,
            data=data,
            toolbar=toolbar,
            table=table,
            parent=parent
        )

        self._isometric_axis = False

        self.line_rk_zgl = []
        self.line_rk_zmin = None
        self.line_rk_zmin_zmax = None
        self.line_rk_zmin_zmax_highlight = None

        self.label_x = self._trad["unit_rk"]
        self.label_y = self._trad["unit_depth"]

        self.before_plot_selected = None
        self.plot_selected = None
        self.after_plot_selected = None
        self.parent = parent
        self._table = table
        self._colors = []

    def onpick(self, event):
        if event.mouseevent.inaxes != self.canvas.axes:
            return
        if event.mouseevent.button.value != 1:
            return

        modifiers = QApplication.keyboardModifiers()
        if modifiers not in [Qt.ControlModifier,
                             Qt.NoModifier,
                             Qt.ShiftModifier]:
            return

        ind, point = self._closest_rk(event)
        if self.parent._table is None:
            return
        self.parent._table.blockSignals(True)
        if modifiers == Qt.ControlModifier:
            rows = list(
                set(
                    (i.row() for i in self.parent.tableView.selectedIndexes())
                )
            )
            if ind in rows:
                rows.remove(ind)
            else:
                rows.append(ind)
            self._select_in_table(rows)
        elif modifiers == Qt.ShiftModifier:
            rows = sorted(list(
                set(
                    (i.row() for i in self.parent.tableView.selectedIndexes())
                )
            ))
            if len(rows) > 0:
                i1 = min(rows[0], rows[-1], ind)
                i2 = max(rows[0], rows[-1], ind)
            else:
                i1 = ind
                i2 = ind
            self._select_range_in_table(i1, i2)
        else:
            self._select_in_table([ind])
        self.parent._table.blockSignals(False)

        return

    def _closest_rk(self, event):

        s = event.artist.get_segments()
        x = [i[0, 0] for i in s]
        mx = event.mouseevent.xdata
        points = enumerate(x)

        def dist_mouse(point):
            x = point[1]
            d = abs(mx - x)
            return d

        closest = min(
            points, key=dist_mouse
        )

        return closest

    def _select_in_table(self, ind):
        if self._table is None:
            return

        self._table.setFocus()
        selection = self._table.selectionModel()
        index = QItemSelection()

        if len(ind) == 0:
            return

        for i in ind:
            index.append(QItemSelectionRange(self._table.model().index(i, 0)))

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

        if len(ind) > 0:
            self._table.scrollTo(self._table.model().index(ind[-1], 0))

    def _select_range_in_table(self, ind1, ind2):
        if self._table is not None:
            self._table.setFocus()
            selection = self._table.selectionModel()
            index = QItemSelection(self._table.model().index(ind1, 0),
                                   self._table.model().index(ind2, 0))
            selection.select(
                index,
                QItemSelectionModel.Rows |
                QItemSelectionModel.ClearAndSelect |
                QItemSelectionModel.Select
            )
            self._table.scrollTo(self._table.model().index(ind2, 0))

    @timer
    def redraw(self, data):
        self._data = data
        self.draw()

    @timer
    def draw(self):
        self.init_axes()

        if self.data is None:
            return

        if len(self.data.profiles) == 0:
            return

        profiles_defined = any(
            filter(
                lambda profile: len(profile) > 0,
                self.data.profiles
            )
        )

        if not profiles_defined:
            self._init = False
            return

        self.draw_z_line()
        self.draw_lr()
        self.draw_gl()
        self.draw_bottom()
        self.draw_profiles_hs(self._data)

        self.idle()
        self._init = True

    def draw_z_line(self):
        rk = self.data.get_rk_complete_profiles()
        z_min = self.data.get_z_min()
        z_max = self.data.get_z_max()

        self._colors, self._style = self.color_highlight()

        self.line_rk_zmin_zmax = self.canvas.axes.vlines(
            x=rk, ymin=z_min, ymax=z_max,
            color=self._colors,
            linestyle=self._style,
            lw=1.,
            picker=10,
        )

    def color_highlight(self):
        rows = sorted(list(
            set(
                (i.row() for i in self.parent.tableView.selectedIndexes())
            )
        ))
        colors = [self.color_plot for row in range(len(self._data))]
        style = ["-" for row in range(len(self._data))]
        if len(rows) > 0:
            for row in rows:
                colors[row] = self.color_plot_current
            if rows[0] > 0:
                colors[rows[0]-1] = self.color_plot_previous
                style[rows[0]-1] = "--"
            if rows[-1] < len(self._data)-1:
                colors[rows[-1]+1] = self.color_plot_next
                style[rows[-1]+1] = "--"
        return colors, style

    def draw_lr(self):
        rk = self.data.get_rk_complete_profiles()
        lz = []
        rz = []

        self.line_lr = []
        for z in self.data.get_z():
            lz.append(z[0])
            rz.append(z[-1])

        line = self.canvas.axes.plot(
            rk, lz,
            color=self.color_plot_river_bottom,
            linestyle="dotted",
            lw=1.,
        )
        self.line_lr.append(line)

        line = self.canvas.axes.plot(
            rk, rz,
            color=self.color_plot_river_bottom,
            linestyle="dotted",
            lw=1.,
        )
        self.line_lr.append(line)

    def draw_gl(self):
        rk = self.data.get_rk_complete_profiles()

        ind = 0
        self.line_rk_zgl = []
        for z in self.data.get_guidelines_z():
            # Is incomplete guideline?
            if len(z) != len(rk):
                continue

            self.line_rk_zgl.append(
                self.canvas.axes.plot(
                    rk, z, lw=1.,
                    color=self.colors[ind % len(self.colors)],
                    linestyle=self.linestyle[ind // len(self.colors)]
                )
            )
            ind += 1

    def draw_bottom(self):
        rk = self.data.get_rk_complete_profiles()
        z_min = self.data.get_z_min()

        self.line_rk_zmin, = self.canvas.axes.plot(
            rk, z_min,
            linestyle=":", lw=1.5,
            color='lightgrey'
        )

    def draw_profiles_hs(self, reach):
        lhs = filter(
            lambda hs: hs._input_reach.reach is reach,
            filter(
                lambda hs: hs._input_reach is not None,
                self._study.river.hydraulic_structures.lst
            )
        )

        for hs in lhs:
            if not hs.enabled:
                continue
            x = hs.input_section.rk
            if x is not None:
                z_min = reach.get_z_min()
                z_max = reach.get_z_max()

                self.canvas.axes.plot(
                    [x, x],
                    [min(z_min), max(z_max)],
                    linestyle="--",
                    lw=1.,
                    color=self.color_plot_previous,
                )

                self.canvas.axes.annotate(
                    " > " + hs.name,
                    (x, max(z_max)),
                    horizontalalignment='left',
                    verticalalignment='top',
                    annotation_clip=True,
                    fontsize=9, color=self.color_plot_previous,
                )

    @timer
    def update(self):
        if not self._init:
            self.draw()
            return

        self.update_lr()
        self.update_gl()
        self.update_current()

        self.update_idle()

    def update_current(self):
        if self._current_data_update:
            self._colors, self._style = self.color_highlight()
            self.line_rk_zmin_zmax.set_colors(self._colors)
            self.line_rk_zmin_zmax.set_linestyle(self._style)

    def update_gl(self):
        if self._current_data_update:
            return

        rk = self.data.get_rk_complete_profiles()
        z_min = self.data.get_z_min()
        z_max = self.data.get_z_max()

        self.line_rk_zmin.set_data(rk, z_min)

#        TODO comprendre à quoi sert ce bout de code
#        ========>
#        self.line_rk_zmin_zmax.remove()
#        self._colors, self._style = self.color_highlight()
#        self.line_rk_zmin_zmax = self.canvas.axes.vlines(
#            x=rk,
#            ymin=z_min,
#            ymax=z_max,
#            color=self._colors,
#            linestyle = self._style,
#            lw=1.
#        )
#        <========

        z_complete = self.data.get_guidelines_z()
        try:
            for i in range(len(self.line_rk_zgl)):
                self.line_rk_zgl[i][0].set_data(
                    rk, z_complete[i]
                )
        except Exception as e:
            logger.warning(f"Failed to update graphic RKZ: {e}")

    def update_lr(self):
        for line in self.line_lr:
            line[0].remove()

        self.draw_lr()
