# Window.py -- Pamhyr
# Copyright (C) 2024-2025  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 -*-

from tools import trace, timer, logger_exception

from PyQt5.QtWidgets import (
    QAction, QListView, QVBoxLayout,
)

from matplotlib.patches import Rectangle

from Modules import Modules

from View.Tools.PamhyrWindow import PamhyrWindow

from View.GeoTIFF.List import ListModel
from View.GeoTIFF.Translate import GeoTIFFTranslate
from View.GeoTIFF.Edit.Window import EditGeoTIFFWindow

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

try:
    import rasterio
    import rasterio.control
    import rasterio.crs
    import rasterio.sample
    import rasterio.vrt
    import rasterio._features

    from rasterio.io import MemoryFile
    _rasterio_loaded = True
except Exception as e:
    print(f"Module 'rasterio' is not available: {e}")
    _rasterio_loaded = False


class GeoTIFFListWindow(PamhyrWindow):
    _pamhyr_ui = "GeoTIFFList"
    _pamhyr_name = "GeoTIFF files"

    def __init__(self, study=None, config=None,
                 parent=None):
        trad = GeoTIFFTranslate()
        name = trad[self._pamhyr_name] + " - " + study.name

        super(GeoTIFFListWindow, self).__init__(
            title=name,
            study=study,
            config=config,
            trad=trad,
            options=[],
            parent=parent
        )

        self.setup_list()
        self.setup_graph()
        self.setup_connections()

    def setup_list(self):
        lst = self.find(QListView, "listView")
        self._list = ListModel(
            list_view=lst,
            data=self._study.river.geotiff,
            undo=self._undo_stack,
            trad=self._trad,
        )

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

        self._plot = PlotXY(
            canvas=self.canvas,
            data=self._study.river.enable_edges(),
            geotiff=self._study.river.geotiff,
            trad=self._trad,
            toolbar=None,
            parent=self
        )
        self._plot_rect = []

        self._plot.update()

    def setup_connections(self):
        if self._study.is_editable():
            self.find(QAction, "action_add").triggered.connect(self.add)
            self.find(QAction, "action_delete").triggered.connect(self.delete)
        else:
            self.find(QAction, "action_add").setEnabled(False)
            self.find(QAction, "action_delete").setEnabled(False)

        self.find(QAction, "action_edit").triggered.connect(self.edit)

        self.find(QListView, "listView")\
            .selectionModel()\
            .selectionChanged\
            .connect(self._update_rectangle)

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

        self.update()

    def update(self):
        self._list.update()
        self._plot.update()

        self._update_rectangle()

    def _update_rectangle(self):
        for rect in self._plot_rect:
            try:
                rect.remove()
            except Exception as e:
                logger_exception(e)

        self._plot_rect = []

        rows = self.selected_rows()
        if len(rows) <= 0:
            return

        for row in rows:
            files = self._study.river.geotiff.files
            if len(files) <= row:
                continue

            geotiff = files[row]
            coord = geotiff.coordinates

            xy = (coord["left"], coord["bottom"])
            width = abs(coord["right"] - coord["left"])
            height = abs(coord["top"] - coord["bottom"])

            rect = Rectangle(
                xy, width, height,
                edgecolor='red', facecolor='none',
                lw=2
            )

            self._plot_rect.append(rect)

            self.canvas.axes.add_patch(
                rect
            )

        self._plot.idle()

    def selected_rows(self):
        lst = self.find(QListView, f"listView")
        return list(map(lambda i: i.row(), lst.selectedIndexes()))

    def add(self):
        rows = self.selected_rows()
        if len(rows) > 0:
            row = rows[0]
        else:
            row = 0

        self._list.add(row)

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

        self._list.delete(rows[0])
        self.update()

    def edit(self):
        rows = self.selected_rows()

        for row in rows:
            geotiff = self._study.river.geotiff.files[row]

            if self.sub_window_exists(
                    EditGeoTIFFWindow,
                    data=[self._study, self._config, geotiff]
            ):
                continue

            win = EditGeoTIFFWindow(
                study=self._study,
                config=self._config,
                geotiff=geotiff,
                trad=self._trad,
                undo=self._undo_stack,
                parent=self,
            )
            win.show()

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

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