#!/usr/bin/env python

# This file is part of chelsa_isimip3b_ba_1km.
#
# chelsa_isimip3b_ba_1km 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 3 of the License, or
# (at your option) any later version.

# chelsa_isimip3b_ba_1km is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with chelsa_isimip3b_ba_1km.  If not, see <https://www.gnu.org/licenses/>.


# import library ##############################################################
from PySAGA import saga_api
import sys
import os
# import argparse
import datetime
import os.path
# import cdsapi
import psutil
import shutil
# import xarray as xr

# *************************************************
# import functions and classes
# *************************************************
# add the directory of this script to allow module search
sys.path.append(os.path.dirname(os.path.abspath(os.path.join(sys.argv[0], '..'))))
from chelsa_isimip3b_ba_1km.functions import auxiliary
from chelsa_isimip3b_ba_1km.functions import saga_functions
from chelsa_isimip3b_ba_1km.functions import ingester
from chelsa_isimip3b_ba_1km.functions import chelsa_data_classes
from chelsa_isimip3b_ba_1km.functions import chelsa


###############################################################################
# auxiliary functions #########################################################
def get_cmd_args():
    # Get the command line arguments
    from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter

    parser = ArgumentParser(
        description='''# This python code is adapted for CHELSA_V2.1_ISIMIP3b
                       the code is adapted to the ISIMIP3b_BA data. It runs the CHELSA algorithm for 
                       min-, max-, and mean temperature, total downwelling solar radiation, and 
                       surface precipitation. The output directory needs the following 
                       subfolders: /rsds, /pr, /tasmin, /tasmax, /tas.
                       Dependencies for ubuntu_18.04:
                       libwxgtk3.0-dev libtiff5-dev libgdal-dev libproj-dev 
                       libexpat-dev wx-common libogdi3.2-dev unixodbc-dev
                       g++ libpcre3 libpcre3-dev wget swig-4.0.1 python2.7-dev 
                       software-properties-common gdal-bin python-gdal 
                       python2.7-gdal libnetcdf-dev libgdal-dev
                       python-pip cdsapi saga_gis-7.6.0
                       All dependencies are resolved in the chelsa_V2.1.cont singularity container
                       Tested with: singularity version 3.3.0-809.g78ec427cc                         ''',
        epilog='''author: Dirk N. Karger, dirk.karger@wsl.ch, Version 2.1''',
        formatter_class=ArgumentDefaultsHelpFormatter
        )
    parser._action_groups.pop()
    required = parser.add_argument_group('required arguments')
    optional = parser.add_argument_group('optional arguments')

    # collect the function arguments
    # required arguments
    required.add_argument(
        '-m', '--model',
        dest='model',
        help='gcm model, string',
        type=str,
        required=True,
    )
    required.add_argument(
        '-exp', '--exp',
        dest='experiment',
        help='experiment, string',
        type=str,
        required=True,
    )
    required.add_argument(
        '-r', '--real', '--realization',
        dest='realization',
        help='experiment, string',
        type=str,
        required=True,
    )
    required.add_argument(
        '-b', '--year',
        dest='year',
        help='year, integer',
        type=int,
        required=True,
    )
    required.add_argument(
        '-c', '--month',
        dest='month',
        help='month, integer',
        type=int,
        required=True,
    )
    required.add_argument(
        '-d', '--day',
        dest='day',
        help='day, integer',
        type=int,
        required=True,
    )
    required.add_argument(
        '-t1', '--array_task_id',
        dest='array_task_id',
        help='slurm array task id to identify the respective band',
        type=int,
        required=True,
    )
    required.add_argument(
        '-i', '--input',
        dest='input',
        help='input directory, string',
        type=str,
        required=True,
    )
    required.add_argument(
        '-o', '--output',
        dest='output',
        help='output directory, string',
        type=str,
        required=True,
    )
    required.add_argument(
        '-t', '--temp',
        dest='temp',
        help='root for temporary directory, string',
        type=str,
        required=True,
    )
    required.add_argument(
        '-w', '--isimip',
        dest='isimip',
        help='isimip input directory, string',
        type=str,
        required=True,
    )
    required.add_argument(
        '-cm', '--cmip',
        dest='cmip',
        help='cmip input directory, string',
        type=str,
        required=True,
    )
    required.add_argument(
        '-au', '--aux',
        dest='aux',
        help='directory with auxilariy files, string',
        type=str,
        required=True,
    )

    # optional arguments
    optional.add_argument(
        '-s', '--srad',
        dest='srad',
        help='srad input directory, string',
        type=str,
        required=False,
        default='None',
    )
    optional.add_argument(
        '-of', '--outputformat',
        dest='outputformat',
        help="output format, either 'GTiff' or 'NetCDF4', string",
        type=str,
        required=False,
        default='netCDF4',
    )
    optional.add_argument(
        '-tz', '--save_lapse_rate',
        dest='save_lapse_rate',
        help='save lapse rate (default: do not), flag',
        action='store_true',
        required=False,
        default=False,
    )

    args = parser.parse_args()
    # reset some arguments
    args.srad = args.input if args.srad.lower() == 'none' else args.srad

    return args
###############################################################################
# main code ###################################################################


# *************************************************
# global parameters
# *************************************************
process = psutil.Process(os.getpid())
saga_functions.Load_Tool_Libraries(True)
saga_api.SG_Set_History_Depth(0)
saga_api.SG_Get_Data_Manager().Delete()

# *************************************************
# Get arguments
# *************************************************
args = get_cmd_args()
for key in vars(args):
    print('  %CLI-parameters: {0} = {1}'.format(key, getattr(args, key)))

year = args.year
month = args.month
day = args.day
# t = '%0d'  % args.array_task_id
output_format = args.outputformat
save_lapse_rate = bool(args.save_lapse_rate)

# get day of the year
dt = datetime.datetime(args.year,
                       args.month,
                       args.day)
tt = dt.timetuple()
dayofyear = tt.tm_yday
dayofyear = str(dayofyear)

print()
# *************************************************
# Set the directories from arguments
# *************************************************

MODEL = args.model
EXPERIMENT = args.experiment
REALIZATION = args.realization
INPUT = args.input
OUTPUT = args.output
TEMP = args.temp
SRAD = args.srad
ISIMIP = args.isimip
W5E5 = args.aux
CMIP = args.cmip
TEMP = os.path.join(TEMP, '{0:04d}-{1:02d}-{2:02d}'.format(year,  month, day))

# clear and/or create TEMP directory
auxiliary.rmdir(TEMP)
auxiliary.mkdir(TEMP)

# *************************************************
# ingest the data
# *************************************************
print('  %Preprocessing:  reading and preparing coarse netCDF data')
print('  %Preprocessing:  {0} - {1} - {2}'.format(MODEL, REALIZATION, EXPERIMENT))
ingester.ingest(dir_isimip=ISIMIP,
                dir_cmip=CMIP,
                dir_temp=TEMP,
                model=MODEL,
                realization=REALIZATION,
                experiment=EXPERIMENT,
                day=day,
                month=month,
                year=year).ingest_data()

# *************************************************
# create the data classes
# *************************************************
print('  %Preprocessing:  reading preprocessed netCDF data into CHELSA data classes')
coarse_data = chelsa_data_classes.Coarse_data(TEMP=TEMP)
dem_data = chelsa_data_classes.Dem_data(INPUT=INPUT)
aux_data = chelsa_data_classes.Aux_data(INPUT=INPUT, W5E5=W5E5)
srad_data = chelsa_data_classes.Srad_data(SRAD=SRAD, dayofyear=dayofyear)


# *************************************************
# run chelsa
# *************************************************
print('  %CHELSA:  running CHELSA')
tas, tasmax, tasmin, rsds, pr = chelsa.chelsa(coarse_data=coarse_data,
                                              dem_data=dem_data,
                                              aux_data=aux_data,
                                              srad_data=srad_data,
                                              TEMP=TEMP)

# *************************************************
# save the results
# *************************************************
print('  %CHELSA:  writing output')
output_variables = ['pr', 'tas', 'tasmax', 'tasmin', 'rsds']
output_variables = output_variables
date_string = '{0:04d}{1:02d}{2:02d}'.format(year, month, day)
metadata = {'lon': {'standard_name': 'longitude', 'axis': 'X',
                    'long_name': 'Lonitude', 'units': 'degrees_east'},
            'lat': {'standard_name': 'latitude', 'axis': 'Y',
                    'long_name': 'Latitude', 'units': 'degrees_north'},
            'time': {'standard_name': 'time', 'axis': 'T'},
            'tas': {'standard_name': 'air_temperature', 'coordinates': 'lat lon',
                    'long_name': 'Near-Surface Air Temperature', 'units': 'K'},
            'tasmin': {'standard_name': 'air_temperature', 'coordinates': 'lat lon',
                       'long_name': 'Daily Minimum Near-Surface Air Temperature', 'units': 'K'},
            'tasmax': {'standard_name': 'air_temperature', 'coordinates': 'lat lon',
                       'long_name': 'Daily Maximum Near-Surface Air Temperature', 'units': 'K'},
            'pr': {'standard_name': 'precipitation_flux', 'coordinates': 'lat lon',
                   'long_name': 'Precipitation', 'units': 'kg m-2 s-1'},
            'rsds': {'standard_name': 'surface_downwelling_shortwave_flux_in_air', 'coordinates': 'lat lon',
                     'long_name': 'Surface Downwelling Shortwave Radiation', 'units': 'W m-2'},
            }

if save_lapse_rate:
    output_variables.append('tz')
    coarse_data.set('tlapse_mean')
    tz = coarse_data.tlapse_mean

for variable in output_variables:
    file_prefix = '{0}_CHELSA_ISIMIP3b_BA_{1}_{2}_{3}'.format(variable, MODEL, REALIZATION, EXPERIMENT)
    auxiliary.mkdir(os.path.join(OUTPUT, variable))
    if output_format.lower() in ['geotiff', 'gtiff', 'tiff', 'tif']:
        fname_out = os.path.join(OUTPUT, variable, '{0}_day_{1}.tif'.format(file_prefix, date_string))
        saga_functions.export_geotiff(OBJ=eval(variable), fname=fname_out)
    elif output_format.lower() in ['netcdf', 'ncdf', 'netcdf4', 'ncdf4']:
        fname_out = os.path.join(OUTPUT, variable, '{0}_day_{1}.nc'.format(file_prefix, date_string))
        saga_functions.export_ncdf(OBJ=eval(variable), variable=variable, fname=fname_out,
                                   metadata={**metadata,
                                             'date': '{0:04d}-{1:02d}-{2:02d}T12:00'.format(year, month, day),
                                             'model': MODEL, 'experiment': EXPERIMENT, 'realization': REALIZATION})
    else:
        raise ValueError('unknown output format')

# if save_lapse_rate:
#     coarse_data.delete('tlapse_mean')

# remove the temporary directory
shutil.rmtree(TEMP)
