# -*- coding: utf-8 -*-
"""Processing script: kerapatan titik panas per petak. Penulis: Badar Mubarok Yogaswara"""
from qgis.core import (QgsProcessingAlgorithm, QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterFeatureSink, QgsProcessingParameterNumber,
                       QgsProcessing, QgsFeatureSink, QgsFeature, QgsField, QgsFields,
                       QgsFeatureRequest, QgsCoordinateTransform, QgsProject,
                       QgsProcessingException, QgsSpatialIndex, Qgis)
from qgis.PyQt.QtCore import QMetaType


class KerapatanHotspot(QgsProcessingAlgorithm):
    PETAK = "PETAK"
    TITIK = "TITIK"
    BATAS = "BATAS"
    HASIL = "HASIL"

    def createInstance(self):
        return KerapatanHotspot()

    def name(self):
        return "kerapatan_hotspot"

    def displayName(self):
        return "Kerapatan titik panas per petak"

    def group(self):
        return "Kehutanan"

    def groupId(self):
        return "kehutanan"

    def shortHelpString(self):
        return ("Menghitung jumlah titik panas di tiap petak dan kerapatannya (titik per hektare). "
                "Petak dengan kerapatan di atas batas diberi nilai Rawan = 1.")

    def initAlgorithm(self, config=None):
        self.addParameter(QgsProcessingParameterFeatureSource(
            self.PETAK, "Layer petak (poligon)", [QgsProcessing.SourceType.TypeVectorPolygon]))
        self.addParameter(QgsProcessingParameterFeatureSource(
            self.TITIK, "Layer titik panas (titik)", [QgsProcessing.SourceType.TypeVectorPoint]))
        self.addParameter(QgsProcessingParameterNumber(
            self.BATAS, "Batas rawan (titik per hektare)",
            type=QgsProcessingParameterNumber.Type.Double, defaultValue=2.0, minValue=0.0))
        self.addParameter(QgsProcessingParameterFeatureSink(self.HASIL, "Petak dengan kerapatan"))

    def processAlgorithm(self, parameters, context, feedback):
        petak = self.parameterAsSource(parameters, self.PETAK, context)
        titik = self.parameterAsSource(parameters, self.TITIK, context)
        batas = self.parameterAsDouble(parameters, self.BATAS, context)
        if petak is None or titik is None:
            raise QgsProcessingException("Layer petak atau titik tidak terbaca.")
        if petak.sourceCrs().mapUnits() != Qgis.DistanceUnit.Meters:
            raise QgsProcessingException("CRS layer petak harus bersatuan meter (luas dibagi 10000 untuk hektare). Proyeksikan ulang dulu, misalnya ke UTM.")

        # titik disamakan ke CRS petak lebih dulu
        ke_petak = QgsCoordinateTransform(titik.sourceCrs(), petak.sourceCrs(), context.transformContext())
        indeks = QgsSpatialIndex()
        geom = {}
        for i, f in enumerate(titik.getFeatures()):
            g = f.geometry()
            g.transform(ke_petak)
            fb = QgsFeature(i)
            fb.setGeometry(g)
            indeks.addFeature(fb)
            geom[i] = g

        kolom = QgsFields(petak.fields())
        kolom.append(QgsField("Jml_Titik", QMetaType.Type.Int))
        kolom.append(QgsField("Titik_per_Ha", QMetaType.Type.Double))
        kolom.append(QgsField("Rawan", QMetaType.Type.Int))
        sink, dest = self.parameterAsSink(parameters, self.HASIL, context, kolom,
                                          petak.wkbType(), petak.sourceCrs())
        if sink is None:
            raise QgsProcessingException("Keluaran tidak bisa dibuat.")

        total = petak.featureCount() or 1
        for n, f in enumerate(petak.getFeatures()):
            if feedback.isCanceled():
                break
            pg = f.geometry()
            kandidat = indeks.intersects(pg.boundingBox())
            jumlah = sum(1 for k in kandidat if pg.contains(geom[k]))
            luas_ha = pg.area() / 10000.0
            kerapatan = jumlah / luas_ha if luas_ha > 0 else 0.0
            baru = QgsFeature(kolom)
            baru.setGeometry(pg)
            baru.setAttributes(f.attributes() + [jumlah, round(kerapatan, 3), 1 if kerapatan > batas else 0])
            sink.addFeature(baru, QgsFeatureSink.Flag.FastInsert)
            feedback.setProgress(100.0 * (n + 1) / total)
        return {self.HASIL: dest}
