from qgis.PyQt.QtCore import (QT_TRANSLATE_NOOP)
from qgis.core import (
  QgsCoordinateReferenceSystem,
  QgsProcessingParameterExtent,
  QgsProcessingParameterDistance,
  QgsProcessingParameterFeatureSink,
  QgsProcessingParameterCrs, 
  QgsProcessingParameterString,
  QgsProcessingParameterNumber
  )
from qgis import processing

from .fetchabstract import fetchabstract

class fetchbuildingja(fetchabstract):
  
  # UIs
  PARAMETERS = {  
    "FETCH_EXTENT": {
      "ui_func": QgsProcessingParameterExtent,
      "ui_args":{
        "description": QT_TRANSLATE_NOOP("fetchbuildingja","Extent for fetching data")
      }
    },
    "TARGET_CRS": {
      "ui_func": QgsProcessingParameterCrs,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchbuildingja","Target CRS (Cartesian coordinates)")
      }
    },
    "BUFFER": {
      "ui_func": QgsProcessingParameterDistance,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchbuildingja","Buffer of the fetch area (using Target CRS)"),
        "defaultValue": 0.0,
        "parentParameterName": "TARGET_CRS"
      }
    },
    "TILEMAP_URL": {
      "ui_func": QgsProcessingParameterString,
      "ui_args": {
        "optional": True,
        "description": QT_TRANSLATE_NOOP("fetchbuildingja","Base-URL of the vector-tile map"),
        "defaultValue": "https://cyberjapandata.gsi.go.jp/xyz/experimental_bvmap/{z}/{x}/{y}.pbf|layername=building|geometrytype=Polygon"
      }
    },
    "TILEMAP_CRS": {
      "ui_func": QgsProcessingParameterCrs,
      "ui_args": {
        "optional": True,
        "description": QT_TRANSLATE_NOOP("fetchbuildingja","CRS of the vector-tile map"),        
        "defaultValue": "EPSG:3857" # must be specified as string, because optional parameter cannot be set as QgsCoordinateReferenceSystem
      }
    },
    "TILEMAP_ZOOM": {
      "ui_func": QgsProcessingParameterNumber,
      "ui_args": {
        "optional": True,
        "description": QT_TRANSLATE_NOOP("fetchbuildingja","Zoom level of the vector-tile map"),
        "type": QgsProcessingParameterNumber.Integer,
        "defaultValue": 16
      }
    },
    "OUTPUT": {
      "ui_func": QgsProcessingParameterFeatureSink,
      "ui_args": {
        "description": QT_TRANSLATE_NOOP("fetchbuildingja","Building")
      }
    }
  }  
  
  # initialization of the algorithm
  def initAlgorithm(self, config):    
    self.initUsingCanvas()
    self.initParameters()
    
  
  # modification of the feature obtained from the map tile
  def writeOutputFeature(self, tx, ty, data_from_tile, data_provider):    
    if data_from_tile.featureCount() <= 0:
      super().writeOutputFeature(tx, ty, data_from_tile, data_provider)
    else:
      # constants
      EQUATOR_M = 40075016.68557849
      N_PIXELS_IN_GSI_VTILE = 4096
      n_pixels_all = N_PIXELS_IN_GSI_VTILE * 2 ** self.TILEMAP_ARGS["Z"]
      meter_per_tile  = EQUATOR_M / 2 ** self.TILEMAP_ARGS["Z"]
      meter_per_pixel = EQUATOR_M / n_pixels_all
      
      # affine transformation to obtain x and y for a given CRS
      affine_parameters = {        
        "INPUT": data_from_tile,
        "DELTA_X":    tx    * meter_per_tile - EQUATOR_M / 2,
        "DELTA_Y": - (ty+1) * meter_per_tile + EQUATOR_M / 2,
        "SCALE_X": meter_per_pixel,
        "SCALE_Y": meter_per_pixel,
        "OUTPUT": "TEMPORARY_OUTPUT"
      }        
      data_modified = processing.run("native:affinetransform", affine_parameters)["OUTPUT"]
      super().writeOutputFeature(tx, ty, data_modified, data_provider)
        
  # execution of the algorithm
  def processAlgorithm(self, parameters, context, feedback):     
    self.setFetchArea(parameters,context,feedback,QgsCoordinateReferenceSystem("EPSG:6668"))
    self.setTileMapArgs(parameters, context, feedback, "Polygon")
    
    # fetch the data from vector map tile
    self.fetchFeaturesFromTile(parameters, context, feedback)
    bldg_raw = self.FETCH_FEATURE
    
    # post processing if there are any features
    if bldg_raw.featureCount() > 0:
      
      # transform
      bldg_transformed = self.transformToTargetCrs(parameters,context,feedback,bldg_raw)
      
      # snap geometry
      bldg_snap = processing.run(
        "native:snapgeometries", 
        {
          "INPUT": bldg_transformed,
          "REFERENCE_LAYER": bldg_transformed,
          "TOLERANCE": 0.1,
          "BEHAVIOR": 0,
          "OUTPUT": "TEMPORARY_OUTPUT"
        }
      )["OUTPUT"]
      
      # dissolve
      bldg_dissolve = self.dissolveFeatures(bldg_snap)
      
      # extract
      bldg_extract = self.extractFeatures(bldg_dissolve)
            
      bldg_final = processing.run(
        "hrisk:initbuilding",{
          "INPUT": bldg_extract,
          "TARGET_CRS": self.parameterAsCrs(parameters, "TARGET_CRS", context),
          "OVERWRITE": True,
          "OUTPUT": "TEMPORARY_OUTPUT"
        }
      )["OUTPUT"]
      
      (sink, dest_id) = self.parameterAsSink(
        parameters, "OUTPUT", context,
        bldg_final.fields(), bldg_final.wkbType(), bldg_final.sourceCrs()
      )
      
      sink.addFeatures(bldg_final.getFeatures())
      
      return {"OUTPUT": dest_id}
      
    else:  
      # set sink and add features with values
      (sink, dest_id) = self.parameterAsSink(
        parameters, "OUTPUT", context,
        bldg_raw.fields(), bldg_raw.wkbType(), bldg_raw.sourceCrs()
      )
      
    return {"OUTPUT": dest_id}
    
  # Post processing; append layers
  def postProcessAlgorithm(self, context, feedback):
    return {}

  def displayName(self):
    return self.tr("Buildings (Ja)")
  
  def group(self):
    return self.tr('Fetch geometries (Ja)')

  def groupId(self):
    return 'fetchgeomja'

  def createInstance(self):
    return fetchbuildingja()
