from qgis.PyQt.QtCore import (QCoreApplication, QT_TRANSLATE_NOOP, QVariant)
from qgis.core import (
  QgsProcessingParameterString,
  QgsProcessingContext,
  QgsProcessingFeedback,
  QgsProject,
  QgsProcessingLayerPostProcessorInterface,
  QgsProcessingParameterExtent,
  QgsProcessingParameterDistance,
  QgsProcessingParameterCrs, 
  QgsProcessingParameterVectorDestination,
  QgsProcessingParameterRasterDestination,
  QgsProcessingParameterNumber,
  QgsProcessingParameterBoolean,
  QgsFeature,
  QgsGeometry,
  QgsField,
  QgsFields,
  QgsWkbTypes,
  QgsProcessingParameterFeatureSink
  )
from qgis import processing

from .fetchabstract import fetchabstract
import uuid


from .fetchabstract import fetchabstract

class fetchall(fetchabstract):
  PARAMETERS = {  
    "FETCH_EXTENT": {
      "ui_func": QgsProcessingParameterExtent,
      "ui_args":{
        "description": QT_TRANSLATE_NOOP("fetchall","Extent for fetching data")
      }
    },
    "TARGET_CRS": {
      "ui_func": QgsProcessingParameterCrs,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Target CRS (Cartesian coordinates)")
      }
    },
    "BUFFER": {
      "ui_func": QgsProcessingParameterDistance,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Buffer of the fetch area (using Target CRS)"),
        "defaultValue": 0.0,
        "parentParameterName": "TARGET_CRS"
      }
    },
    
    "SET_RECEIVER_FACADE": {
      "ui_func": QgsProcessingParameterBoolean,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Set receiver at building facade?"),
        "defaultValue": False
      }
    },
    
    "SET_RECEIVER_DELAUNAYGRID": {
      "ui_func": QgsProcessingParameterBoolean,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Set receiver at delaynay grid point?"),
        "defaultValue": False
      }
    },
    
    "USERNAME": {
      "ui_func": QgsProcessingParameterString,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchdemrastersrtm","Username to login SRTM data system")
      }
    },
    "PASSWORD": {
      "ui_func": QgsProcessingParameterString,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchdemrastersrtm","Password to login SRTM data system")
      }
    },
    "DELTA": {
      "advanced": True,
      "ui_func": QgsProcessingParameterNumber,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Distance between receivers (m)"),
        "type": QgsProcessingParameterNumber.Double,
        "minValue": 1.0, "defaultValue": 10.0, "maxValue": 100.0
      },
      "n_mdl": "delta"
    },
    "HEIGHT": {
      "advanced": True,
      "ui_func": QgsProcessingParameterNumber,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Height of receivers (m)"),
        "type": QgsProcessingParameterNumber.Double,
        "minValue": 0.01, "defaultValue": 4.0, "maxValue": 100.0
      },
      "n_mdl": "height"
    },    
    "MAX_PROP_DIST": {
      "advanced": True,
      "ui_func": QgsProcessingParameterNumber,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Maximum propagation distance between sources and receivers (m)"),
        "type": QgsProcessingParameterNumber.Double,
        "minValue": 100.0, "defaultValue": 500.0, "maxValue": 2000.0
      },
      "n_mdl": "maxPropDist"
    },
    "ROAD_WIDTH": {
      "advanced": True,
      "ui_func": QgsProcessingParameterNumber,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Road width (m), where no receivers will be set closer than it"),
        "type": QgsProcessingParameterNumber.Double,
        "minValue": 1.0, "defaultValue": 2.0, "maxValue": 20.0
      },
      "n_mdl": "roadWidth"
    },
    "MAX_AREA": {
      "advanced": True,
      "ui_func": QgsProcessingParameterNumber,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Maximum trianglar area (m2)"),
        "type": QgsProcessingParameterNumber.Double,
        "minValue": 10.0, "defaultValue": 500.0, "maxValue": 10000.0
      },
      "n_mdl": "maxArea"
    },
    "FETCH_AREA": {
      "ui_func": QgsProcessingParameterFeatureSink,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Fetch area" )
      },
      "visibleByDefault": False
    },
    "ROAD": {
      "ui_func": QgsProcessingParameterVectorDestination,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Roads" )
      },
      "visibleByDefault": True
    },
    "BUILDING": {
      "ui_func": QgsProcessingParameterVectorDestination,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Buildings" )
      },
      "visibleByDefault": True
    },    
    "RECEIVER_FACADE": {
      "ui_func": QgsProcessingParameterVectorDestination,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Receivers at facade" )
      }     ,
      "visibleByDefault": False
    },    
    "RECEIVER_DELAUNAY": {
      "ui_func": QgsProcessingParameterVectorDestination,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Receivers of delaunay" )
      },
      "visibleByDefault": False
    },
    "TRIANGLE_DELAUNAY": {
      "ui_func": QgsProcessingParameterVectorDestination,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Triangles of delaunay" )
      },
      "visibleByDefault": False
    },    
    "DEM": {
      "ui_func": QgsProcessingParameterVectorDestination,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Elevation point (DEM)" )
      },
      "visibleByDefault": False
    },    
    "DEM_RASTER": {
      "ui_func": QgsProcessingParameterRasterDestination,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchall","Elevation raster (DEM)" )
      },
      "visibleByDefault": False
    }
  }
  
  PROC_RESULTS = {}
  GRP_ID = ""
    
  
  def initAlgorithm(self, config):    
    self.initUsingCanvas()
    self.initParameters()
    
    
  
  def fetchRoad(self, parameters, context, feedback):
    self.PROC_RESULTS["ROAD"] = processing.run(
      "hrisk:fetchroadosm",
      {
        "FETCH_EXTENT": self.parameterAsString(parameters, "FETCH_EXTENT", context),# Note that parameterAsExtent is NG because CRS is not included
        "TARGET_CRS": self.parameterAsCrs(parameters, "TARGET_CRS", context),
        "BUFFER": self.parameterAsDouble(parameters, "BUFFER",context),
        "OUTPUT": self.parameterAsOutputLayer(parameters, "ROAD", context)
      },
      context = context,
      feedback = feedback
    )["OUTPUT"]
    
    
  def fetchBuilding(self, parameters, context, feedback):
    
    self.PROC_RESULTS["BUILDING"] = processing.run(
      "hrisk:fetchbuildingosm",
      {
        "FETCH_EXTENT": self.parameterAsString(parameters, "FETCH_EXTENT", context),# Note that parameterAsExtent is NG because CRS is not included
        "TARGET_CRS": self.parameterAsCrs(parameters, "TARGET_CRS", context),
        "BUFFER": self.parameterAsDouble(parameters, "BUFFER",context),
        "OUTPUT": self.parameterAsOutputLayer(parameters, "BUILDING", context)
      },
      context = context,
      feedback = feedback
    )["OUTPUT"]
    
    
  def fetchDem(self, parameters, context, feedback):
    dem_processing = processing.run(
      "hrisk:fetchdemrastersrtm",
      {
        "FETCH_EXTENT": self.parameterAsString(parameters, "FETCH_EXTENT", context),
        "TARGET_CRS": self.parameterAsCrs(parameters, "TARGET_CRS", context),
        "BUFFER": self.parameterAsDouble(parameters, "BUFFER",context),
        "USERNAME": self.parameterAsString(parameters, "USERNAME", context),
        "PASSWORD": self.parameterAsString(parameters, "PASSWORD", context),
        "OUTPUT": self.parameterAsOutputLayer(parameters, "DEM", context),
        "OUTPUT_RASTER": self.parameterAsOutputLayer(parameters, "DEM_RASTER", context)
      },
      context = context,
      feedback = feedback
    )
    self.PROC_RESULTS["DEM"] = dem_processing["OUTPUT"]
    self.PROC_RESULTS["DEM_RASTER"] = dem_processing["OUTPUT_RASTER"]
    
  def setReceiverFacade(self, parameters, context, feedback):
    self.PROC_RESULTS["RECEIVER_FACADE"] = processing.run(
      "hrisk:receiverfacade",
      {
        "BUILDING": self.PROC_RESULTS["BUILDING"],
        "SOURCE": self.PROC_RESULTS["ROAD"],
        "FENCE_EXTENT": self.parameterAsString(parameters, "FETCH_EXTENT", context),# Note that parameterAsExtent is NG because CRS is not included
        "DELTA": self.parameterAsDouble(parameters, "DELTA", context),
        "HEIGHT": self.parameterAsDouble(parameters, "HEIGHT", context),
        "OUTPUT": self.parameterAsOutputLayer(parameters, "RECEIVER_FACADE", context)
      },
      context = context,
      feedback = feedback
    )["OUTPUT"]
  

  def setReceiverDelaunayGrid(self, parameters, context, feedback):
    delaunay_processing = processing.run(
      "hrisk:receiverdelaunaygrid",
      {
        "BUILDING": self.PROC_RESULTS["BUILDING"],
        "SOURCE": self.PROC_RESULTS["ROAD"],
        "FENCE_EXTENT": self.parameterAsString(parameters, "FETCH_EXTENT", context),# Note that parameterAsExtent is NG because CRS is not included
        "MAX_PROP_DIST": self.parameterAsDouble(parameters, "MAX_PROP_DIST", context),
        "ROAD_WIDTH": self.parameterAsDouble(parameters, "ROAD_WIDTH", context),
        "MAX_AREA": self.parameterAsDouble(parameters, "MAX_AREA", context),
        "HEIGHT": self.parameterAsDouble(parameters, "HEIGHT", context),
        "OUTPUT": self.parameterAsOutputLayer(parameters, "RECEIVER_DELAUNAY", context),
        "TRIANGLE": self.parameterAsOutputLayer(parameters, "TRIANGLE_DELAUNAY", context)
      },
      context = context,
      feedback = feedback
    )
    
    self.PROC_RESULTS["RECEIVER_DELAUNAY"] = delaunay_processing["OUTPUT"]
    self.PROC_RESULTS["TRIANGLE_DELAUNAY"] = delaunay_processing["TRIANGLE"]

  def setFetchArea(self, parameters: dict, context: QgsProcessingContext, feedback: QgsProcessingFeedback) -> None:
    super().setFetchArea(parameters, context, feedback)
    
    fa_flds = QgsFields()
    fa_flds.append(QgsField("extent", QVariant.String))
    (sink, dest_id) = self.parameterAsSink(
      parameters, "FETCH_AREA", context, fa_flds, QgsWkbTypes.Polygon, self.FETCH_AREA.crs()
    )
    
    ft = QgsFeature(fa_flds)
    ft.setGeometry(QgsGeometry.fromRect(self.FETCH_AREA))
    ft["extent"] = self.parameterAsString(parameters, "FETCH_EXTENT", context)
    
    sink.addFeature(ft)
    
    self.PROC_RESULTS["FETCH_AREA"] = dest_id
    
  
  def processAlgorithm(self, parameters, context, feedback):    
    feedback.pushInfo(self.tr("Configurations"))
    feedback.setProgress(0)    
    
    self.GRP_ID = "geom_" + str(uuid.uuid4())[:6]
    
    feedback.pushInfo(self.tr("Set fetch area"))
    self.setFetchArea(parameters,context,feedback)
    feedback.setProgress(5)      
        
    feedback.pushInfo(self.tr("Fetch geometry of roads"))
    self.fetchRoad(parameters, context, feedback)
    feedback.setProgress(15)    
    
    feedback.pushInfo(self.tr("Fetch geometry of buildings and estimate the population"))
    self.fetchBuilding(parameters, context, feedback)
    feedback.setProgress(25)
    
    feedback.pushInfo(self.tr("Fetch geometry of DEM"))
    self.fetchDem(parameters, context, feedback)
    feedback.setProgress(50)    
  
    if self.parameterAsBoolean(parameters, "SET_RECEIVER_FACADE", context):    
      feedback.pushInfo(self.tr("Set receivers at building facade"))
      self.setReceiverFacade(parameters, context, feedback)
      feedback.setProgress(75) 
         
    if self.parameterAsBoolean(parameters, "SET_RECEIVER_DELAUNAYGRID", context):        
      feedback.pushInfo(self.tr("Set receivers of delaunay grid"))
      self.setReceiverDelaunayGrid(parameters, context, feedback)
      
    feedback.setProgress(100)
      
    return self.PROC_RESULTS
    
  # Post processing; append layers
  def postProcessAlgorithm(self, context, feedback):
    global geom_postprocessors
    geom_postprocessors = []
    
    QgsProject.instance().layerTreeRoot().insertGroup(0, self.GRP_ID)
    
    layer_dict = context.layersToLoadOnCompletion()
    ipp = 0
    for i, path in enumerate(layer_dict.keys()):
      if len([k for k, v in self.PROC_RESULTS.items() if v == path]) == 1:
        vis = self.PARAMETERS.get([k for k, v in self.PROC_RESULTS.items() if v == path][0]).get("visibleByDefault")
        geom_postprocessors.append(geomPostProcessor(self.GRP_ID, vis, context.layerToLoadOnCompletionDetails(path).postProcessor()))
        context.layerToLoadOnCompletionDetails(path).setPostProcessor(geom_postprocessors[ipp])
        ipp = ipp + 1
    return {}


  def displayName(self):
    return self.tr("All geometries and set receivers")

  def group(self):
    return self.tr('Fetch geometries')

  def groupId(self):
    return 'fetchgeometry'

  def createInstance(self):
    return fetchall()

class geomPostProcessor (QgsProcessingLayerPostProcessorInterface):
  def __init__(self, group_name, visibility = True, existing_postprocessor = None):
    self.group_name = group_name
    self.visibility = visibility
    self.existing_pp = existing_postprocessor
    super().__init__()
    
  def postProcessLayer(self, layer, context, feedback):
    if self.existing_pp != None:
      self.existing_pp.postProcessLayer(layer, context, feedback)
    root = QgsProject.instance().layerTreeRoot()
    vl = root.findLayer(layer.id())
    vl.setItemVisibilityChecked(self.visibility)
    vl_clone = vl.clone()
    grp = root.findGroup(self.group_name)
    if grp != None:
      parent = vl.parent()
      grp.insertChildNode(0, vl_clone)
      parent.removeChildNode(vl)