# -*- coding: utf-8 -*-
"""
/***************************************************************************
 QProto
                                 A QGIS plugin
 QProto Algorithm
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2018-04-19
        copyright            : (C) Matteo Ghetta (Faunalia); 2026 update by Stefano Campus
        email                : skampus@gmail.com
 ***************************************************************************/

/***************************************************************************
 * *
 * 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 2 of the License, or     *
 * (at your option) any later version.                                   *
 * *
 ***************************************************************************/
"""

__author__ = 'Matteo Ghetta (Faunalia); 2026 update by Stefano Campus'
__date__ = '2018-04-19'
__copyright__ = '(C) 2018 by Matteo Ghetta (Faunalia); 2026 update by Stefano Campus'

import math
from osgeo import gdal
import numpy as np
import os
from qgis.core import (
    QgsPointXY, QgsFeature, QgsGeometry,
    QgsRasterBandStats, QgsContrastEnhancement,
    QgsRasterShader, QgsColorRampShader,
    QgsSingleBandPseudoColorRenderer
)
from qgis.PyQt.QtGui import QColor


def _unique_breaks(values, is_integer=False):
    unique_vals = []
    for v in values:
        if is_integer:
            vv = int(round(v))
            if vv not in unique_vals:
                unique_vals.append(vv)
        else:
            vv = round(float(v), 2)
            if not any(abs(vv - existing) < 1e-9 for existing in unique_vals):
                unique_vals.append(vv)
    return unique_vals

def _count_interval_labels(breaks):
    if not breaks:
        return [], []

    positive_breaks = sorted({int(v) for v in breaks if int(v) > 0})
    if not positive_breaks:
        return [1], ['1']

    interval_upper_bounds = []
    interval_labels = []
    lower_bound = max(1, positive_breaks[0])

    for upper_bound in positive_breaks[1:]:
        if upper_bound < lower_bound:
            continue
        interval_upper_bounds.append(upper_bound)
        interval_labels.append('{}-{}'.format(lower_bound, upper_bound))
        lower_bound = upper_bound + 1

    last_upper_bound = positive_breaks[-1]
    if not interval_upper_bounds or interval_upper_bounds[-1] != last_upper_bound:
        interval_upper_bounds.append(last_upper_bound)
        interval_labels.append('{}-{}'.format(lower_bound, last_upper_bound))

    return interval_upper_bounds, interval_labels

def map2pixel(geoTransform, extent):
    (xmin, xmax, ymin, ymax) = (extent.xMinimum(), extent.xMaximum(), extent.yMinimum(), extent.yMaximum())
    (Xmin, Ymax) = gdal.ApplyGeoTransform(gdal.InvGeoTransform(geoTransform), xmin, ymax)
    (Xmax, Ymin) = gdal.ApplyGeoTransform(gdal.InvGeoTransform(geoTransform), xmax, ymin)
    return (int(Xmin), int(Ymax), int(Xmax), int(Ymin))

def pixel2map(geoTransform, X, Y):
    (x, y) = gdal.ApplyGeoTransform(geoTransform, X + 0.5, Y + 0.5)
    return (x, y)

def triangolo(point1, f, name, dist, alfa):
    points = [point1]
    angle1 = math.radians(90-(f[name] + alfa))
    dist_x, dist_y = (dist * math.cos(angle1), dist * math.sin(angle1))
    point2 = QgsPointXY(point1[0] + dist_x, point1[1] + dist_y)
    points.append(point2)
    angle2 = math.radians(90-((f[name]) - alfa))
    dist_x, dist_y = (dist * math.cos(angle2), dist * math.sin(angle2))
    point3 = QgsPointXY(point1[0] + dist_x, point1[1] + dist_y)
    points.append(point3)
    poly = QgsFeature()
    trGeom = QgsGeometry()
    poly.setGeometry(trGeom.fromPolygonXY([points]))
    return poly

def OutStyle(inRaster, Results, visualName):
    """
    Applica una simbologia colorata a un raster, salva il QML corrispondente e restituisce il path.
    Per i raster 'count' e 'sum_weight' NON viene applicata una legenda continua.
    """
    if not inRaster or not inRaster.isValid():
        print(f"[QPROTO] Raster '{visualName}' non valido.")
        return

    lower_name = visualName.lower()
    fileName = os.path.splitext(visualName)[0]
    qml_path = os.path.join(Results, fileName + '.qml')

    provider = inRaster.dataProvider()
    extent = inRaster.extent()
    stats = provider.bandStatistics(1, QgsRasterBandStats.All, extent, 0)

    min_val = stats.minimumValue
    max_val = stats.maximumValue
    value_range = max_val - min_val

    print(f"DEBUG min: {min_val}, max: {max_val}")

    if lower_name.startswith('count'):
        breaks = _unique_breaks([
            min_val,
            min_val + (value_range * 0.25),
            min_val + (value_range * 0.50),
            min_val + (value_range * 0.75),
            max_val
        ], is_integer=True)
    else:
        breaks = _unique_breaks([
            min_val,
            min_val + (value_range * 0.25),
            min_val + (value_range * 0.50),
            min_val + (value_range * 0.75),
            max_val
        ], is_integer=False)

    # Fallback for flat rasters (all pixels same value)
    if not breaks:
        breaks = [0]
    print(f"[QPROTO] Breaks: {breaks}")

    try:
        inRaster.setContrastEnhancement(QgsContrastEnhancement.StretchToMinimumMaximum)
    except TypeError:
        # Backward compatibility with older QGIS APIs which expected a second positional argument.
        inRaster.setContrastEnhancement(QgsContrastEnhancement.StretchToMinimumMaximum, True)

    shader = QgsRasterShader()
    color_ramp = QgsColorRampShader()

    # Verifica se usare legenda continua o discreta
    if not lower_name.startswith(("count", "sum_weight")):
        color_ramp.setColorRampType(QgsColorRampShader.Interpolated)
    else:
        color_ramp.setColorRampType(QgsColorRampShader.Discrete)

    ramp_items = []
    if lower_name.startswith('count'):
        interval_values, labels = _count_interval_labels(breaks)
    elif lower_name.startswith(('susceptibility', 'w_en', 'w_tot_en', 'sum_weight')):
        interval_values = breaks
        labels = ["very low", "limited", "medium", "high", "very high"][:len(breaks)]
    else:
        interval_values = breaks
        labels = [str(v) for v in breaks]

    colors = ['#2b83ba', '#abdda4', '#ffffbf', '#fdae61', '#d7191c']
    for value, color, label in zip(interval_values, colors, labels):
        print(value, label)
        ramp_items.append(QgsColorRampShader.ColorRampItem(value, QColor(color), label))
    
    print(ramp_items)

    color_ramp.setColorRampItemList(ramp_items)
    shader.setRasterShaderFunction(color_ramp)

    renderer = QgsSingleBandPseudoColorRenderer(inRaster.dataProvider(), 1, shader)
    inRaster.setRenderer(renderer)
    inRaster.triggerRepaint()

    try:
        os.makedirs(Results, exist_ok=True)
        inRaster.saveNamedStyle(qml_path)
        print(f"[QPROTO] Stile salvato: {qml_path}")
    except Exception as e:
        print(f"[QPROTO] Errore nel salvataggio stile: {e}")
        return None

    if not os.path.exists(qml_path):
        print(f"[QPROTO] File QML non trovato dopo il salvataggio: {qml_path}")
        return None

    with open(qml_path, 'r', encoding='utf-8') as f:
        content = f.read()
        content = content.replace('classificationMax="nan"', f'classificationMax="{max_val}"')
        content = content.replace('classificationMin="nan"', f'classificationMin="{min_val}"')

    with open(qml_path, 'w', encoding='utf-8') as f:
        f.write(content)

    print("Sostituzione completata.")

    return qml_path
