# -*- coding: utf-8 -*-
"""A 2x2 grid of synchronized map canvases for sample labeling.

Each panel shows a different view of the SAME location: by default
NDVI, NDWI, ESA WorldCover 2021, and Google Satellite. All four pan and
zoom together, so one point can be judged from several angles at once.
"""

from qgis.PyQt.QtWidgets import (
    QWidget, QGridLayout, QVBoxLayout, QComboBox, QLabel, QSizePolicy,
)
from qgis.PyQt.QtGui import QColor
from qgis.gui import QgsMapCanvas
from qgis.core import (
    QgsProject, QgsCoordinateReferenceSystem, QgsRectangle,
)

from .. import gee_utils as gu

# preset names looked for in the project / built on demand
PRESETS = ["NDVI", "NDWI", "ESA WorldCover 2021", "Google Satellite",
           "True color", "Classified LULC", "(none)"]


class QuadView(QWidget):
    """2x2 synchronized canvases. Call set_center(point, scale) to move all."""

    def __init__(self, iface, state, parent=None):
        super().__init__(parent)
        self.iface = iface
        self.state = state
        self.canvases = []
        self.selectors = []
        self.notes = []
        self._syncing = False
        self._build()

    # ------------------------------------------------------------ UI
    def _build(self):
        grid = QGridLayout(self)
        grid.setSpacing(3)
        grid.setContentsMargins(0, 0, 0, 0)
        defaults = ["NDVI", "NDWI", "True color", "Google Satellite"]
        for i in range(4):
            box = QWidget()
            v = QVBoxLayout(box)
            v.setContentsMargins(0, 0, 0, 0)
            v.setSpacing(1)

            cb = QComboBox()
            cb.addItems(PRESETS)
            cb.setCurrentText(defaults[i])
            cb.currentTextChanged.connect(
                lambda name, k=i: self.set_panel_layer(k, name))
            v.addWidget(cb)

            canvas = QgsMapCanvas()
            canvas.setCanvasColor(QColor(30, 30, 30))
            # A canvas with an INVALID CRS renders black. Fall back to
            # Web Mercator when the project has no usable CRS.
            crs = QgsProject.instance().crs()
            if not crs.isValid():
                crs = QgsCoordinateReferenceSystem("EPSG:3857")
            canvas.setDestinationCrs(crs)
            canvas.setMinimumSize(180, 150)
            canvas.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
            canvas.extentsChanged.connect(
                lambda k=i: self._on_extent_changed(k))
            v.addWidget(canvas, 1)

            note = QLabel("")
            note.setStyleSheet("color:#c66; font-size:10px;")
            note.setWordWrap(True)
            v.addWidget(note)
            self.notes.append(note)

            grid.addWidget(box, i // 2, i % 2)
            self.canvases.append(canvas)
            self.selectors.append(cb)

    # ------------------------------------------------------------ sync
    def _on_extent_changed(self, k):
        if self._syncing:
            return
        self._syncing = True
        try:
            ext = self.canvases[k].extent()
            for j, c in enumerate(self.canvases):
                if j != k:
                    c.setExtent(ext)
                    c.refresh()
        finally:
            self._syncing = False

    def set_center(self, point, scale):
        """Center every panel on the same point at the same scale.
        Builds the extent explicitly - zoomScale() alone does nothing on a
        canvas that has never had a valid extent (a black-panel cause)."""
        self._syncing = True
        try:
            for c in self.canvases:
                crs = c.mapSettings().destinationCrs()
                # half-width in map units for the requested scale
                if crs.isGeographic():
                    # ~1 degree = 111320 m; canvas is ~ 0.28 mm per pixel
                    half = (scale * 0.0001) / 111320.0 * 500.0
                else:
                    half = scale * 0.05          # metres
                ext = QgsRectangle(point.x() - half, point.y() - half,
                                   point.x() + half, point.y() + half)
                c.setExtent(ext)
                c.zoomScale(scale)
                c.refresh()
        finally:
            self._syncing = False

    # ------------------------------------------------------------ layers
    def _find_layer(self, name):
        """Find a project layer matching a preset name."""
        want = name.lower()
        proj = QgsProject.instance()
        cands = list(proj.mapLayers().values())
        if want == "google satellite":
            for lyr in cands:
                if lyr.name().lower().startswith("google satellite"):
                    return lyr
            return gu.add_google_satellite()
        if want == "esa worldcover 2021":
            for lyr in cands:
                if "worldcover" in lyr.name().lower():
                    return lyr
            return self._make_worldcover()
        if want == "classified lulc":
            lid = self.state.get("classified_layer_id")
            if lid:
                lyr = proj.mapLayer(lid)
                if lyr:
                    return lyr
            for lyr in cands:
                if lyr.name().lower().startswith("lulc "):
                    return lyr
            return None
        if want == "true color":
            for lyr in cands:
                nm = lyr.name().lower()
                if nm.startswith("stack ") or "sentinel" in nm \
                        or "composite" in nm:
                    return lyr
            return self._make_index_layer(None)
        if want in ("ndvi", "ndwi"):
            for lyr in cands:
                if lyr.name().lower() == "index: " + want:
                    return lyr
            return self._make_index_layer(name)
        return None

    def _make_worldcover(self):
        """Add ESA WorldCover 2021 as a GEE tile layer."""
        try:
            ee = gu.init_ee()
            wc = (ee.ImageCollection("ESA/WorldCover/v200").first()
                  .select("Map"))
            vis = {"min": 10, "max": 100,
                   "palette": ["006400", "ffbb22", "ffff4c", "f096ff",
                               "fa0000", "b4b4b4", "f0f0f0", "0064c8",
                               "0096a0", "00cf75", "fae6a0"]}
            return gu.add_ee_image_layer(wc, vis, "ESA WorldCover 2021")
        except (ImportError, ModuleNotFoundError, RuntimeError):  # GEE/Import
            return None

    def _make_index_layer(self, index_name):
        """Build an index preview layer from the in-memory GEE stack."""
        stack = self.state.get("stack")
        if stack is None:
            return None
        try:
            if index_name is None:                 # true color
                kind = self.state.get("kind", "optical")
                if kind == "sar":
                    vis = {"bands": ["VV", "VH", "VV"],
                           "min": -25, "max": 0}
                elif kind == "embedding":
                    vis = {"bands": ["A01", "A16", "A09"],
                           "min": -0.3, "max": 0.3}
                else:
                    vis = {"bands": ["red", "green", "blue"],
                           "min": 0.02, "max": 0.35, "gamma": 2.0}
                return gu.add_ee_image_layer(stack, vis, "True color view")
            bands = self.state.get("band_names") or []
            if index_name not in bands:
                return None
            pal = {"NDVI": ["8c510a", "f5f5f5", "01665e"],
                   "NDWI": ["8c510a", "f5f5f5", "0571b0"]}.get(
                       index_name, ["000000", "ffffff"])
            return gu.add_ee_image_layer(
                stack.select(index_name),
                {"min": -1, "max": 1, "palette": pal},
                "Index: " + index_name)
        except (ImportError, ModuleNotFoundError, RuntimeError):  # GEE/Import
            return None

    def set_panel_layer(self, k, name, points_layer=None):
        """Set which layer panel k shows (always with the points on top)."""
        canvas = self.canvases[k]
        layers = []
        msg = ""
        if points_layer is not None:
            layers.append(points_layer)
        if name != "(none)":
            base = self._find_layer(name)
            if base is None:
                msg = ("'{}' not available - build the stack in Tool 1 "
                       "(and tick that index) first.".format(name))
            elif not base.isValid():
                msg = "'{}' failed to load.".format(name)
            else:
                layers.append(base)
        if k < len(self.notes):
            self.notes[k].setText(msg)
        canvas.setLayers(layers)
        canvas.refresh()

    def refresh_all(self, points_layer=None):
        for k, cb in enumerate(self.selectors):
            self.set_panel_layer(k, cb.currentText(), points_layer)
