"""
/***************************************************************************
 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.                                   *
 *                                                                         *
 ***************************************************************************/

"The Sky View Factor algorithm can be used to generate pixel wise sky view factor (SVF) "
"using ground and building digital surface models (DSM). Optionally, vegetation DSMs could also be used. "
"By definition, SVF is the ratio of the radiation received (or emitted) by a planar surface to the "
"radiation emitted (or received) by the entire hemispheric environment (Watson and Johnson 1987). "
"It is a dimensionless measure between zero and one, representing totally obstructed and free spaces, "
"respectively. The methodology that is used to generate SVF here is described in Lindberg and Grimmond (2010).\n"
"-------------\n"
"Lindberg F, Grimmond CSB (2010) Continuous sky view factor maps from high resolution urban digital elevation models. Clim Res 42:177–183\n"
"Watson ID, Johnson GT (1987) Graphical estimation of skyview-factors in urban environments. J Climatol 7: 193–197"
"------------\n"
"Full manual available via the <b>Help</b>-button."

"https://umep-docs.readthedocs.io/en/latest/pre-processor/Urban%20Geometry%20Sky%20View%20Factor%20Calculator.html"
"""

__author__ = "Fredrik Lindberg"
__date__ = "2020-04-02"
__copyright__ = "(C) 2020 by Fredrik Lindberg"

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

__revision__ = "$Format:%H$"

# %%
import logging
import os
import zipfile
from pathlib import Path

import numpy as np

from umep import class_configs, common
from umep.functions import svf_functions as svf

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


# %%
def generate_svf(
    dsm_path: str,
    bbox: list[int],
    out_dir: str,
    dem_path: str | None = None,
    cdsm_path: str | None = None,
    trans_veg_perc: float = 3,
    trunk_ratio_perc: float = 25,
    amax_local_window_m: int = 100,
    amax_local_perc: float = 99.9,
):
    out_path = Path(out_dir)
    out_path.mkdir(parents=True, exist_ok=True)
    out_path_str = str(out_path)

    # Open the DSM file
    dsm, dsm_trf, dsm_crs, dsm_nd = common.load_raster(dsm_path, bbox)
    dsm_pix_size = dsm_trf[1]
    dsm_scale = 1 / dsm_pix_size

    dem = None
    if dem_path is not None:
        dem, dem_trf, dem_crs, _dem_nd = common.load_raster(dem_path, bbox)
        assert dem.shape == dsm.shape, "Mismatching raster shapes for DSM and DEM."
        assert np.allclose(dsm_trf, dem_trf), "Mismatching spatial transform for DSM and DEM."
        assert dem_crs == dsm_crs, "Mismatching CRS for DSM and DEM."

    use_cdsm = False
    cdsm = None
    if cdsm_path is not None:
        use_cdsm = True
        cdsm, cdsm_trf, cdsm_crs, _cdsm_nd = common.load_raster(cdsm_path, bbox)
        assert cdsm.shape == dsm.shape, "Mismatching raster shapes for DSM and CDSM."
        assert np.allclose(dsm_trf, cdsm_trf), "Mismatching spatial transform for DSM and CDSM."
        assert cdsm_crs == dsm_crs, "Mismatching CRS for DSM and CDSM."

    # veg transmissivity as percentage
    if not (0 <= trans_veg_perc <= 100):
        raise ValueError("Vegetation transmissivity should be a number between 0 and 100")

    trans_veg = trans_veg_perc / 100.0
    trunk_ratio = trunk_ratio_perc / 100.0

    dsm, dem, cdsm, tdsm, amax = class_configs.raster_preprocessing(
        dsm,
        dem,
        cdsm,
        None,
        trunk_ratio,
        dsm_pix_size,
        amax_local_window_m=amax_local_window_m,
        amax_local_perc=amax_local_perc,
    )

    common.save_raster(
        out_path_str + "/input-dsm.tif",
        dsm,
        dsm_trf,
        dsm_crs,
        dsm_nd,
    )
    if dem is not None:
        common.save_raster(
            out_path_str + "/input-dem.tif",
            dem,
            dsm_trf,
            dsm_crs,
            dsm_nd,
        )
    if use_cdsm:
        common.save_raster(
            out_path_str + "/input-cdsm.tif",
            cdsm,
            dsm_trf,
            dsm_crs,
            dsm_nd,
        )
        common.save_raster(
            out_path_str + "/input-tdsm.tif",
            tdsm,
            dsm_trf,
            dsm_crs,
            dsm_nd,
        )

    # compute
    ret = svf.svfForProcessing153(dsm, cdsm, tdsm, dsm_scale, use_cdsm, amax)

    svfbu = ret["svf"]
    svfbuE = ret["svfE"]
    svfbuS = ret["svfS"]
    svfbuW = ret["svfW"]
    svfbuN = ret["svfN"]

    # Save the rasters using rasterio
    common.save_raster(out_path_str + "/" + "svf.tif", svfbu, dsm_trf, dsm_crs)
    common.save_raster(out_path_str + "/" + "svfE.tif", svfbuE, dsm_trf, dsm_crs)
    common.save_raster(out_path_str + "/" + "svfS.tif", svfbuS, dsm_trf, dsm_crs)
    common.save_raster(out_path_str + "/" + "svfW.tif", svfbuW, dsm_trf, dsm_crs)
    common.save_raster(out_path_str + "/" + "svfN.tif", svfbuN, dsm_trf, dsm_crs)

    # Create or update the ZIP file
    zip_filepath = out_path_str + "/" + "svfs.zip"
    if os.path.isfile(zip_filepath):
        os.remove(zip_filepath)

    with zipfile.ZipFile(zip_filepath, "a") as zippo:
        zippo.write(out_path_str + "/" + "svf.tif", "svf.tif")
        zippo.write(out_path_str + "/" + "svfE.tif", "svfE.tif")
        zippo.write(out_path_str + "/" + "svfS.tif", "svfS.tif")
        zippo.write(out_path_str + "/" + "svfW.tif", "svfW.tif")
        zippo.write(out_path_str + "/" + "svfN.tif", "svfN.tif")

    # Remove the individual TIFF files after zipping
    os.remove(out_path_str + "/" + "svf.tif")
    os.remove(out_path_str + "/" + "svfE.tif")
    os.remove(out_path_str + "/" + "svfS.tif")
    os.remove(out_path_str + "/" + "svfW.tif")
    os.remove(out_path_str + "/" + "svfN.tif")

    if use_cdsm == 0:
        svftotal = svfbu
    else:
        # Report the vegetation-related results
        svfveg = ret["svfveg"]
        svfEveg = ret["svfEveg"]
        svfSveg = ret["svfSveg"]
        svfWveg = ret["svfWveg"]
        svfNveg = ret["svfNveg"]
        svfaveg = ret["svfaveg"]
        svfEaveg = ret["svfEaveg"]
        svfSaveg = ret["svfSaveg"]
        svfWaveg = ret["svfWaveg"]
        svfNaveg = ret["svfNaveg"]

        # Save vegetation rasters
        common.save_raster(out_path_str + "/" + "svfveg.tif", svfveg, dsm_trf, dsm_crs)
        common.save_raster(out_path_str + "/" + "svfEveg.tif", svfEveg, dsm_trf, dsm_crs)
        common.save_raster(out_path_str + "/" + "svfSveg.tif", svfSveg, dsm_trf, dsm_crs)
        common.save_raster(out_path_str + "/" + "svfWveg.tif", svfWveg, dsm_trf, dsm_crs)
        common.save_raster(out_path_str + "/" + "svfNveg.tif", svfNveg, dsm_trf, dsm_crs)
        common.save_raster(out_path_str + "/" + "svfaveg.tif", svfaveg, dsm_trf, dsm_crs)
        common.save_raster(out_path_str + "/" + "svfEaveg.tif", svfEaveg, dsm_trf, dsm_crs)
        common.save_raster(out_path_str + "/" + "svfSaveg.tif", svfSaveg, dsm_trf, dsm_crs)
        common.save_raster(out_path_str + "/" + "svfWaveg.tif", svfWaveg, dsm_trf, dsm_crs)
        common.save_raster(out_path_str + "/" + "svfNaveg.tif", svfNaveg, dsm_trf, dsm_crs)

        # Add vegetation rasters to the ZIP file
        with zipfile.ZipFile(zip_filepath, "a") as zippo:
            zippo.write(out_path_str + "/" + "svfveg.tif", "svfveg.tif")
            zippo.write(out_path_str + "/" + "svfEveg.tif", "svfEveg.tif")
            zippo.write(out_path_str + "/" + "svfSveg.tif", "svfSveg.tif")
            zippo.write(out_path_str + "/" + "svfWveg.tif", "svfWveg.tif")
            zippo.write(out_path_str + "/" + "svfNveg.tif", "svfNveg.tif")
            zippo.write(out_path_str + "/" + "svfaveg.tif", "svfaveg.tif")
            zippo.write(out_path_str + "/" + "svfEaveg.tif", "svfEaveg.tif")
            zippo.write(out_path_str + "/" + "svfSaveg.tif", "svfSaveg.tif")
            zippo.write(out_path_str + "/" + "svfWaveg.tif", "svfWaveg.tif")
            zippo.write(out_path_str + "/" + "svfNaveg.tif", "svfNaveg.tif")

        # Remove the individual TIFF files after zipping
        os.remove(out_path_str + "/" + "svfveg.tif")
        os.remove(out_path_str + "/" + "svfEveg.tif")
        os.remove(out_path_str + "/" + "svfSveg.tif")
        os.remove(out_path_str + "/" + "svfWveg.tif")
        os.remove(out_path_str + "/" + "svfNveg.tif")
        os.remove(out_path_str + "/" + "svfaveg.tif")
        os.remove(out_path_str + "/" + "svfEaveg.tif")
        os.remove(out_path_str + "/" + "svfSaveg.tif")
        os.remove(out_path_str + "/" + "svfWaveg.tif")
        os.remove(out_path_str + "/" + "svfNaveg.tif")

        # Calculate final total SVF
        svftotal = svfbu - (1 - svfveg) * (1 - trans_veg)

    # Save the final svftotal raster
    common.save_raster(out_path_str + "/" + "svf_total.tif", svftotal, dsm_trf, dsm_crs)

    # Save shadow matrices as compressed npz
    shmat = ret["shmat"]
    vegshmat = ret["vegshmat"]
    vbshvegshmat = ret["vbshvegshmat"]

    np.savez_compressed(
        out_path_str + "/" + "shadowmats.npz",
        shadowmat=shmat,
        vegshadowmat=vegshmat,
        vbshmat=vbshvegshmat,
    )
