# -*- coding: utf-8 -*-

"""
/***************************************************************************
 ProcessingUMEP
                                 A QGIS plugin
 UMEP for processing toolbox
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2020-04-02
        copyright            : (C) 2020 by Fredrik Lindberg
        email                : fredrikl@gvc.gu.se
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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__ = 'Fredrik Lindberg'
__date__ = '2022-02-07'
__copyright__ = '(C) 2020 by Fredrik Lindberg'

# This will get replaced with a git SHA1 when you do a git archive

__revision__ = '$Format:%H$'

from qgis.PyQt.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsProcessing,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterFile,
                       QgsProcessingParameterString,
                       QgsProcessingParameterNumber,
                       QgsProcessingParameterFolderDestination,
                       QgsProcessingParameterEnum,
                       QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterField,
                       QgsProcessingException,
                       QgsProcessingParameterBoolean,
                       QgsVectorLayer)

from qgis.PyQt.QtGui import QIcon
from osgeo import osr
# from osgeo.gdalconst import *
import os
import numpy as np
import inspect
from pathlib import Path
import sys
import json
# from ..util.umep_uwg_export_component import create_uwgdict, get_uwg_file


class ProcessingTARGETPrepareAlgorithm(QgsProcessingAlgorithm):
    """
    This algorithm is a processing version of UWG Prepare
    """

    INPUT_POLYGONLAYER = 'INPUT_POLYGONLAYER'
    ID_FIELD = 'ID_FIELD'
    INPUT_MORPH = 'INPUT_MORPH'
    INPUT_LC = 'INPUT_LC'
    UMEP_LC = 'UMEP_LC'
    FRAC_IRR = 'FRAC_IRR'
    FRAC_CONC = 'FRAC_CONC'
    SITE_NAME = 'SITE_NAME'
    OUTPUT_DIR = 'OUTPUT_DIR'

    
    def initAlgorithm(self, config):
        
        self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT_POLYGONLAYER,
            self.tr('Vector polygon grid'), [QgsProcessing.TypeVectorPolygon]))
        self.addParameter(QgsProcessingParameterField(self.ID_FIELD,
            self.tr('ID field'),'', self.INPUT_POLYGONLAYER, QgsProcessingParameterField.Numeric))
        self.addParameter(QgsProcessingParameterFile(self.INPUT_MORPH,
            self.tr('Building morphology file (.txt)'), extension='txt'))
        self.addParameter(QgsProcessingParameterFile(self.INPUT_LC,
            self.tr('Land cover fraction file (.txt)'), extension='txt'))
        self.addParameter(QgsProcessingParameterBoolean(self.UMEP_LC,
            self.tr("Use standard UMEP land cover grid (fractions below is used)"), defaultValue=True))
        self.addParameter(QgsProcessingParameterNumber(self.FRAC_IRR, 
            self.tr('Fraction Irrigated grass taken from Grass land cover class'), 
            QgsProcessingParameterNumber.Double,
            QVariant(0.20), False, minValue=0.0, maxValue=1.0))
        self.addParameter(QgsProcessingParameterNumber(self.FRAC_CONC, 
            self.tr('Fraction Concrete taken from Paved land cover class'), 
            QgsProcessingParameterNumber.Double,
            QVariant(0.25), False, minValue=0.0, maxValue=1.0))
        self.addParameter(QgsProcessingParameterString(self.SITE_NAME, 
            self.tr('Site name')))
        self.addParameter(QgsProcessingParameterFolderDestination(self.OUTPUT_DIR, 
            self.tr('Output folder')))

        self.plugin_dir = os.path.dirname(__file__)


    def processAlgorithm(self, parameters, context, feedback):
        # InputParameters 
        inputPolygonlayer = self.parameterAsVectorLayer(parameters, self.INPUT_POLYGONLAYER, context)
        idField = self.parameterAsFields(parameters, self.ID_FIELD, context)
        morphFile = self.parameterAsString(parameters, self.INPUT_MORPH, context)
        lcFile = self.parameterAsString(parameters, self.INPUT_LC, context)
        umepLC = self. parameterAsBool(parameters, self.UMEP_LC, context)
        fracIrr = self.parameterAsDouble(parameters, self.FRAC_IRR, context)
        fracConc = self.parameterAsDouble(parameters, self.FRAC_CONC, context)
        siteName = self.parameterAsString(parameters, self.SITE_NAME, context)
        outputDir = self.parameterAsString(parameters, self.OUTPUT_DIR, context)

        if parameters['OUTPUT_DIR'] == 'TEMPORARY_OUTPUT':
            if not (os.path.isdir(outputDir)):
                os.mkdir(outputDir)
        
        # temporary fix for mac, ISSUE #15
        pf = sys.platform
        if pf == 'darwin' or pf == 'linux2' or pf == 'linux':
            if not os.path.exists(outputDir + '/' + siteName):
                os.makedirs(outputDir + '/' + siteName)

        poly_field = idField
        vlayer = inputPolygonlayer
        nGrids = vlayer.featureCount()
        index = 1
        feedback.setProgressText("Number of grids to process: " + str(nGrids))

        map_units = vlayer.crs().mapUnits()
        if not map_units == 0 or map_units == 1 or map_units == 2:
            raise QgsProcessingException("Could not identify the map units of the polygon layer CRS.")

        arrmat = np.empty((1, 10))
        header = 'FID,roof,road,watr,conc,Veg,dry,irr,H,W'
        numformat = '%3d, %5.3f, %5.3f, %5.3f, %5.3f, %5.3f, %5.3f, %5.3f, %5.3f, %5.3f'

        # Test for metre units in vector CRS
        grid_crs = osr.SpatialReference()
        grid_crs.ImportFromWkt(vlayer.crs().toWkt())
        grid_unit = grid_crs.GetAttrValue('UNIT')
        feedback.setProgressText("Length unit of vector layer: " + str(grid_unit))
        possible_units_metre = ['metre', 'Metre', 'metres', 'Metres', 'meter', 'Meter', 'meters', 'Meters', 'm'] 
        if not grid_unit in possible_units_metre:
            raise QgsProcessingException('Error! Vector polygon projection is not in meters. Please reproject.')

        #Get resolution of grid (res)
        for feature in vlayer.getFeatures():
            geom = feature.geometry()
            bb = geom.boundingBox()
            res = np.round(bb.xMaximum() - bb.xMinimum())   
            break

        #Saving parameters file
        with open(self.plugin_dir + '/parametersfortarget.json', "r") as jsn:
            param = json.load(jsn)

        param['res']['value'] = res 

        


        #Start loop of polygon grids
        ##land cover and morphology
        index = 0
        for feature in vlayer.getFeatures():
            feedback.setProgress(int((index * 100) / nGrids))
            if feedback.isCanceled():
                feedback.setProgressText("Calculation cancelled")
                break
            index += 1

            feat_id = int(feature.attribute(poly_field[0]))

            with open(lcFile) as file:
                next(file)
                for line in file:
                    split = [float(x) for x in line.split()]
                    if feat_id == int(split[0]):
                        if umepLC:
                            conc = split[1] * fracConc
                            road = split[1] - conc 
                            irr = split[5] * fracIrr
                            dry = split [6] + split[5] - irr # bare soil is classed as dry grass
                        else:
                            road = split[1]
                            conc = split[9]
                            dry = split[5] + split[6] # bare soil is classed as dry grass
                            irr = split[8]

                        roof = split[2]
                        veg = split[3] + split[4]
                        watr = split[7]
                        
                        break
            
            with open(morphFile) as file:
                next(file)
                for line in file:
                    split = [float(x) for x in line.split()]
                    if feat_id == int(split[0]):
                        H = split[3]
                        wai = split[8]
                        pai = split[1]
                        if pai == 1: #response to #109
                            feedback.pushWarning('Building fraction = 1 in grid: ' + str(feat_id) + '. This is unlikely. Check your DSM and DEM. Land cover roof fraction will be used.')
                            pai = roof
                            if pai == 1:
                                feedback.pushWarning('Building fraction form land cover = 1 in grid: ' + str(feat_id) + '. This is unlikely. Check your data.')
                                pai = 0.99
                        if pai == 0 or wai == 0: #response to #112
                            W = res
                        else:
                            HW = (wai*pai)/(2*pai*(1-pai))
                            W = H / HW
                            if W > res:
                                W = res #W cannot be larger than grid resolution
                        
                        break
            
            arr = [feat_id, roof, road, watr, conc, veg, dry, irr, H, W]

            arrmat = np.vstack([arrmat, arr])

        arrmatsave = arrmat[1: arrmat.shape[0], :]
        print(arrmatsave)
        # adding zavg (and z0m later) in parameter file. Weighted avg from lc-file
        zavg = 0
        fracsum = sum(arrmatsave[:, 1])
        for i in range(0,index):
            zavg = zavg + arrmatsave[i, 8] * (arrmatsave[i, 1] / fracsum) 
        
        param['zavg']['value'] = zavg
        
        jsonout = json.dumps(param, indent=4)#'C:/temp/targettests/my_site/parameterstest.json'
        path = os.path.join(outputDir, siteName)
        os.makedirs(path, exist_ok=True)  # create directory if it doesn’t exist. Response to #767
        with open(path + '/parameters.json', "w") as jsn2:
            jsn2.write(jsonout)

        #creating folders and saving
        if not os.path.exists(outputDir + '/' + siteName + '/' + 'input' + '/' + 'LC'):
            os.makedirs(outputDir + '/' + siteName + '/' + 'input' + '/' + 'LC')

        if not os.path.exists(outputDir + '/' + siteName + '/' + 'input' + '/' + 'MET'):
            os.makedirs(outputDir + '/' + siteName + '/' + 'input' + '/' + 'MET')

        np.savetxt(outputDir + '/' + siteName + '/' + 'input' + '/' + 'LC' + '/lc_target.txt', arrmatsave,
                            fmt=numformat, header=header, comments='')
        
        feedback.setProgressText("Process finished")

        return {self.OUTPUT_DIR: outputDir}

    def name(self):
        return 'Urban Heat Island: TARGET Prepare'

    def displayName(self):
        return self.tr(self.name())

    def group(self):
        return self.tr(self.groupId())

    def groupId(self):
        return 'Pre-Processor'

    def shortHelpString(self):
        return self.tr('<b>THIS PLUGIN IS EXPERIMENTAL</b>'
        '\n'
        '<b>TARGET</b> (The Air-temperature Response to Green blue-infrastructure Evaluaition Tool) is a simple modelling framework used to examine '
        'intra urban climate. It has specifically been developed as an efficient, easy-to-use model albe to investigate '
        'heat mitigation effects of green and blue infrastructure within urban areas but can also be used to model the canopy urban heat island '
        '<a href="https://gmd.copernicus.org/articles/12/785/2019/">(Broadbent et al. 2019)</a>. '
        'Possibilities to model mutiple grids or a single location is available.\n'
        '\n'
        'This particular tool prepare input data used for the TARGET model found in the Processor in UMEP for processing.'
        '\n'
        '----------------------\n'
        'Full manual is available via the <b>Help</b>-button.')

    def helpUrl(self):
        url = "https://umep-docs.readthedocs.io/en/latest/pre-processor/Urban%20Heat%20Island%20TARGET%20Prepare.html"
        return url

    def tr(self, string):
        return QCoreApplication.translate('Processing', string)

    def icon(self):
        cmd_folder = Path(os.path.split(inspect.getfile(inspect.currentframe()))[0]).parent
        icon = QIcon(str(cmd_folder) + "/icons/icon_uwg.png")
        return icon

    def createInstance(self):
        return ProcessingTARGETPrepareAlgorithm()