# 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 -*-

import logging

from Modules import Modules
from View.Tools.PamhyrWindow import PamhyrWindow

from PyQt5.QtWidgets import (
    QLabel, QPlainTextEdit, QPushButton, QCheckBox,
    QFileDialog, QVBoxLayout, QDoubleSpinBox,
)

from PyQt5.QtCore import (
    QSettings
)

from View.GeoTIFF.Translate import GeoTIFFTranslate
from View.GeoTIFF.UndoCommand import (
    SetCommand
)

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

logger = logging.getLogger()


class EditGeoTIFFWindow(PamhyrWindow):
    _pamhyr_ui = "GeoTIFF"
    _pamhyr_name = "Edit GeoTIFF"

    def __init__(self, study=None, config=None, geotiff=None,
                 trad=None, undo=None, parent=None):

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

        self._geotiff = geotiff
        self._file_name = geotiff.file_name

        self._hash_data.append(self._geotiff)

        self._undo = undo

        self.setup_values()
        self.setup_graph()
        self.setup_connection()

    def setup_graph(self):
        self.canvas = MplCanvas(width=5, height=4, dpi=100)
        self.canvas.setObjectName("canvas")
        self.plot_layout = self.find(QVBoxLayout,
                                     "verticalLayout_geotiff")
        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(),
            trad=self._trad,
            toolbar=None,
            parent=self
        )
        self.plot.update()

        self._plot_img = None

        memfile = self._geotiff.memfile
        if memfile is not None:
            self.draw_geotiff(memfile=memfile)

    def setup_values(self):
        self.set_check_box("checkBox", self._geotiff.enabled)
        self.set_line_edit_text("lineEdit_name", self._geotiff.name)
        self.set_line_edit_text("lineEdit_description",
                                self._geotiff.description)

        bounds = list(self._geotiff.coordinates.values())
        self._set_values_from_bounds(bounds)
        self._set_default_values_from_bounds(bounds)
        self._reset_spinboxes()

        if self._study.is_read_only():
            self.set_check_box_enable("checkBox", False)
            self.set_line_edit_enable("lineEdit_name", False)
            self.set_line_edit_enable("lineEdit_description", False)

            for button in ["import", "bottom", "top", "left", "right"]:
                self.find(QPushButton, f"pushButton_{button}")\
                    .setEnabled(False)

            for spin in ["bottom", "top", "left", "right"]:
                self.find(QDoubleSpinBox, f"doubleSpinBox_{spin}")\
                    .setEnabled(False)

    def _set_values_from_bounds(self, bounds):
        self._values = {
            "bottom": bounds[0],
            "top": bounds[1],
            "left": bounds[2],
            "right": bounds[3],
        }

    def _set_default_values_from_bounds(self, bounds):
        self._values_default = {
            "bottom": bounds[0],
            "top": bounds[1],
            "left": bounds[2],
            "right": bounds[3],
        }

    def _reset_spinboxes(self):
        for key in self._values:
            self._reset_spinbox(key)

    def _reset_spinbox(self, key):
        self.set_double_spin_box(
            f"doubleSpinBox_{key}", self._values_default[key]
        )

    def setup_connection(self):
        self.find(QPushButton, "pushButton_cancel")\
            .clicked.connect(self.close)
        self.find(QPushButton, "pushButton_ok")\
            .clicked.connect(self.accept)
        self.find(QPushButton, "pushButton_import")\
            .clicked.connect(self._import)

        self.find(QPushButton, "pushButton_bottom")\
            .clicked.connect(lambda: self._reset_spinbox("bottom"))
        self.find(QPushButton, "pushButton_top")\
            .clicked.connect(lambda: self._reset_spinbox("top"))
        self.find(QPushButton, f"pushButton_left")\
            .clicked.connect(lambda: self._reset_spinbox("left"))
        self.find(QPushButton, f"pushButton_right")\
            .clicked.connect(lambda: self._reset_spinbox("right"))

        self.find(QDoubleSpinBox, f"doubleSpinBox_bottom")\
            .valueChanged.connect(
                lambda: self.update_values_from_spinbox("bottom")
            )
        self.find(QDoubleSpinBox, f"doubleSpinBox_top")\
            .valueChanged.connect(
                lambda: self.update_values_from_spinbox("top")
            )
        self.find(QDoubleSpinBox, f"doubleSpinBox_left")\
            .valueChanged.connect(
                lambda: self.update_values_from_spinbox("left")
            )
        self.find(QDoubleSpinBox, f"doubleSpinBox_right")\
            .valueChanged.connect(
                lambda: self.update_values_from_spinbox("right")
            )

    def update_values_from_spinbox(self, key):
        self._values[key] = self.get_double_spin_box(f"doubleSpinBox_{key}")

        left = self._values["left"]
        right = self._values["right"]
        bottom = self._values["bottom"]
        top = self._values["top"]

        self._plot_img.set_extent((left, right, bottom, top))
        self.plot.idle()

    def draw_geotiff(self, memfile=None):
        if not _rasterio_loaded:
            return

        if memfile is None:
            if self._file_name == "":
                return

            with rasterio.open(self._file_name) as data:
                img = data.read()
                b = data.bounds[:]  # left, bottom, right, top

                if b[2] > b[0] and b[1] < b[3]:
                    coord = [b[1], b[3], b[0], b[2]]
                else:
                    xlim = self.canvas.axes.get_xlim()
                    ylim = self.canvas.axes.get_ylim()
                    coord = ylim + xlim

                self._set_values_from_bounds(coord)
                self._set_default_values_from_bounds(coord)
        else:
            with memfile.open() as gt:
                img = gt.read()

        if self._plot_img is not None:
            self._plot_img.remove()

        left = self._values["left"]
        right = self._values["right"]
        bottom = self._values["bottom"]
        top = self._values["top"]

        self._plot_img = self.canvas.axes.imshow(
            img.transpose((1, 2, 0)),
            extent=(left, right, bottom, top)
        )

        self.plot.idle()
        self._reset_spinboxes()

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

        file_types = [
            self._trad["file_geotiff"],
            self._trad["file_all"],
        ]

        filename, _ = QFileDialog.getOpenFileName(
            self,
            self._trad["open_file"],
            "",
            ";; ".join(file_types),
            options=options
        )

        if filename != "":
            self._file_name = filename
            self.draw_geotiff()

    def accept(self):
        if self._study.is_editable():
            is_enabled = self.get_check_box("checkBox")
            name = self.get_line_edit_text("lineEdit_name")
            description = self.get_line_edit_text("lineEdit_description")

            coord_bottom = self.get_double_spin_box("doubleSpinBox_bottom")
            coord_top = self.get_double_spin_box("doubleSpinBox_top")
            coord_left = self.get_double_spin_box("doubleSpinBox_left")
            coord_right = self.get_double_spin_box("doubleSpinBox_right")

            self._undo.push(
                SetCommand(
                    self._geotiff, enabled=is_enabled,
                    name=name, description=description,
                    coordinates_bottom=coord_bottom,
                    coordinates_top=coord_top,
                    coordinates_left=coord_left,
                    coordinates_right=coord_right,
                    file_name=self._file_name,
                )
            )

            self._propagate_update(key=Modules.GEOTIFF)

        self.close()
