// SPDX-License-Identifier: LGPL-3.0-or-later
/***********************************************************************************/
/*  Copyright 2013 WSL Institute for Snow and Avalanche Research    SLF-DAVOS      */
/***********************************************************************************/
/* This file is part of MeteoIO.
    MeteoIO is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    MeteoIO 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with MeteoIO.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <meteoio/dataGenerators/GeneratorAlgorithms.h>
#include <meteoio/MeteoProcessor.h> //required to init the time restrictions

#include <meteoio/dataGenerators/AllSkyLWGenerator.h>
#include <meteoio/dataGenerators/AllSkySWGenerator.h>
#include <meteoio/dataGenerators/ClearSkyLWGenerator.h>
#include <meteoio/dataGenerators/ClearSkySWGenerator.h>
#include <meteoio/dataGenerators/ConstGenerator.h>
#include <meteoio/dataGenerators/ESOLIPGenerator.h>
#include <meteoio/dataGenerators/HumidityGenerator.h>
#include <meteoio/dataGenerators/IswrAlbedoGenerator.h>
#include <meteoio/dataGenerators/MeteoIndexGenerator.h>
#include <meteoio/dataGenerators/PrecSplitting.h>
#include <meteoio/dataGenerators/RadiationComponents.h>
#include <meteoio/dataGenerators/SinGenerator.h>
#include <meteoio/dataGenerators/StdPressGenerator.h>
#include <meteoio/dataGenerators/TauCLDGenerator.h>
#include <meteoio/dataGenerators/TsGenerator.h>
#include <meteoio/dataGenerators/WindComponents.h>

namespace mio {

/**
 * @page generators Data generators
 * New data can be generated based on some parametrizations at two very different stages:
 *    + in \ref data_editing "Input Data Editing", by calling a *CREATE* editing command;
 *    + when the requested data could not be provided as last resort, as data generator.
 *
 * In the first case, the parametrizations are used just after reading the data with the 
 * input plugins and before any filtering and/or resampling has taken place. It is however 
 * necessary to provide a few additional arguments, as documented in \ref data_editing "Input Data Editing".
 *
 * The second case takes place once the data has been read, filtered and resampled, if some data points are still missing.
 * These are either a few isolated periods (a sensor was not functioning) that are too large for performing
 * a statistical temporal interpolation or that a meteorological parameter was not even measured. In such a case,
 * we generate data, generally relying on some parametrization using other meteorological parameters. In a few
 * cases, even fully arbitrary data might be helpful (replacing missing value by a given constant so a model can
 * run over the data gap).
 *
 * Finally, it is possible to disable a given data generator / creator for specific stations, using the <i>exclude</i> or <i>only</i>
 * options followed by a list of station IDs (see example below) as well as restrict the operation of a data generator / creator
 * to specific time ranges using the **when** option followed by a comma delimited list of date intervals (represented by 
 * two ISO formatted dates seperated by ' - ').
 *
 * @note it is generally not advised to use data generators in combination with spatial interpolations as this would
 * potentially mix measured and generated values in the resulting grid. It is therefore advised to turn the data generators
 * off and let the spatial interpolations algorithms adjust to the amount of measured data.
 * @note it is also possible to make a copy of a given parameter under a different name. This is explained in section
 * \ref data_editing "Input Data Editing".
 *
 * @section generators_section Data generators section
 * The data generators are defined per meteorological parameter. They are applied to all stations
 * (if using multiple meteorological stations). If multiple data generators are specified for each parameter,
 * they would be used in the order of declaration, meaning that only the data points that could not be
 * generated by the first generator would be tentatively generated by the second generator, etc. Please also keep
 * in mind that at this stage, all data <b>must be in SI</b> units!
 * @code
 * [InputEditing]
 * #calling the parametrizations very early on, just after reading the raw data
 * *::edit1            = CREATE
 * *::arg1::algorithgm = CST
 * *::arg1::param      = TAU_CLD	;create a new TAU_CLD parameter set at constant 0.5
 * *::arg1::param      = 0.5
 *
 * [Generators]
 * #calling the parametrizations after everything else happened 
 * #but before spatial interpolations
 * RH::generator1  = CST
 * RH::arg1::value = .7
 *
 * P::generator1 = STD_PRESS
 * P::arg1::when = 2020-07-01 - 2020-07-10 , 2020-07-20T12:00 - 2020-08-01
 *
 * ILWR::generator1    = AllSky_LW
 * ILWR::arg1::exclude = DAV3 DAV5
 * ILWR::generator2    = ClearSky_LW
 * ILWR::arg2::only    = *WFJ *DAV
 * @endcode
 *
 * @section generators_keywords Available generators
 * The keywords defining the algorithms are the following:
 * - NONE: does nothing, it allows overwriting an existing generator in an included ini file
 * - STD_PRESS: standard atmospheric pressure as a function of the elevation of each station (see StandardPressureGenerator)
 * - HUMIDITY: generate any of the humidity parameters from the others (see HumidityGenerator)
 * - TS_OLWR: surface temperature from Outgoing Long Wave Radiation (see TsGenerator)
 * - ISWR_ALBEDO: ISWR from RSWR or RSWR from ISWR with either a snow or a soil albedo, depending on HS (see IswrAlbedoGenerator)
 * - CST: constant value as provided in argument (see ConstGenerator)
 * - SIN: sinusoidal variation (see SinGenerator)
 * - CLEARSKY_LW: use a clear sky model to generate ILWR from TA, RH (see ClearSkyLWGenerator)
 * - ALLSKY_LW: use an all sky model to generate ILWR from TA, RH and cloudiness (see AllSkyLWGenerator)
 * - CLEARSKY_SW: use a clear sky model to generate ISWR from TA, RH (see ClearSkySWGenerator)
 * - ALLSKY_SW: generate the incoming short wave radiation from the potential radiation, corrected for cloudiness if possible (see AllSkySWGenerator)
 * - TAU_CLD: generate the atmospheric transmissivity based on cloud cover fraction (see TauCLDGenerator)
 * - ESOLIP: generate precipitation from snow height changes (see ESOLIPGenerator)
 * - PRECSPLITTING: generate the precipitation phase and/or convert between amount / phase and split precipitation (see PrecSplitting)
 * - RADCOMPONENTS: generate the global radiation ISWR from the direct and diffuse components (see RadiationComponents)
 * - WINDCOMPONENTS: generate the wind velocity and/or wind direction from the U and V wind components (see WindComponents)
 * - METEOINDEX: generate common meteorological indices (see MeteoIndex)
 *
 * @section generators_biblio Bibliography
 * The data generators have been inspired by the following papers:
 * - Brutsaert -- <i>"On a Derivable Formula for Long-Wave Radiation From Clear Skies"</i>, Journal of Water Resources
 * Research, <b>11</b>, No. 5, October 1975, pp 742-744.
 * - Prata -- <i>"A new long-wave formula for estimating downward clear-sky radiation at the surface"</i>, Q. J. R. Meteorolo. Soc., <b>122</b>, 1996, pp 1127-1151.
 * - Dilley and O'Brien -- <i>"Estimating downward clear sky long-wave irradiance at the surface from screen temperature and precipitable water"</i>, Q. J. R. Meteorolo. Soc., Vol. 124, 1998, doi:10.1002/qj.49712454903
 * - Clark & Allen -- <i>"The estimation of atmospheric radiation for clear and cloudy skies"</i>, Proceedings of the second national passive solar conference, <b>2</b>, 1978, p 676.
 * - Tang et al. -- <i>"Estimates of clear night sky emissivity in the Negev Highlands, Israel"</i>, Energy Conversion and Management, <b>45.11</b>, 2004, pp 1831-1843.
 * - Idso -- <i>"A set of equations for full spectrum and 8 to 14 um and 10.5 to 12.5 um thermal radiation from cloudless skies"</i>, Water Resources Research, <b>17</b>, 1981, pp 295-304.
 * - Kasten and Czeplak -- <i>"Solar and terrestrial radiation dependent on the amount and type of cloud"</i>, 1980, Solar energy, 24.2, pp 177-189
 * - Omstedt -- <i>"A coupled one-dimensional sea ice-ocean model applied to a semi-enclosed basin"</i>, Tellus, <b>42 A</b>, 568-582, 1990, DOI:10.1034/j.1600-0870.1990.t01-3-00007.
 * - Konzelmann et al. -- <i>"Parameterization of global and longwave incoming radiation for the Greenland Ice Sheet."</i> Global and Planetary change <b>9.1</b> (1994): 143-164.
 * - Crawford and Duchon -- <i>"An Improved Parametrization for Estimating Effective Atmospheric Emissivity for Use in Calculating Daytime
 * Downwelling Longwave Radiation"</i>, Journal of Applied Meteorology, <b>38</b>, 1999, pp 474-480
 * - Unsworth and Monteith -- <i>"Long-wave radiation at the ground"</i>, Q. J. R. Meteorolo. Soc., Vol. 101, 1975, pp 13-24
 * - Meeus -- <i>"Astronomical Algorithms"</i>, second edition, 1998, Willmann-Bell, Inc., Richmond, VA, USA
 * - Mair et al. -- <i>" ESOLIP–estimate of solid and liquid precipitation at sub-daily time resolution by combining snow height
 * and rain gauge measurements"</i>, Hydrology and Earth System Sciences Discussions, <b>10(7)</b>, 8683-8714, 2013.
 *
 */

const double GeneratorAlgorithm::soil_albedo = .23; //grass
const double GeneratorAlgorithm::snow_albedo = .85; //snow
const double GeneratorAlgorithm::snow_thresh = .1; //if snow height greater than this threshold -> snow albedo

GeneratorAlgorithm* GeneratorAlgorithmFactory::getAlgorithm(const Config& cfg, const std::string& i_algoname, const std::string& i_section, const std::vector< std::pair<std::string, std::string> >& vecArgs)
{
	std::string algoname(i_algoname);
	IOUtils::toUpper(algoname);
	const double TZ = cfg.get("TIME_ZONE", "Input");

	if (algoname == "CST"){
		return new ConstGenerator(vecArgs, i_algoname, i_section, TZ);
	} else if (algoname == "SIN"){
		return new SinGenerator(vecArgs, i_algoname, i_section, TZ);
	} else if (algoname == "STD_PRESS"){
		return new StandardPressureGenerator(vecArgs, i_algoname, i_section, TZ);
	} else if (algoname == "HUMIDITY"){
		return new HumidityGenerator(vecArgs, i_algoname, i_section, TZ);
	} else if (algoname == "TAU_CLD"){
		return new TauCLDGenerator(vecArgs, i_algoname, i_section, TZ, cfg);
	} else if (algoname == "TS_OLWR"){
		return new TsGenerator(vecArgs, i_algoname, i_section, TZ);
	} else if (algoname == "ISWR_ALBEDO"){
		return new IswrAlbedoGenerator(vecArgs, i_algoname, i_section, TZ);
	} else if (algoname == "CLEARSKY_LW"){
		return new ClearSkyLWGenerator(vecArgs, i_algoname, i_section, TZ);
	} else if (algoname == "ALLSKY_LW"){
		return new AllSkyLWGenerator(vecArgs, i_algoname, i_section, TZ, cfg);
	} else if (algoname == "ALLSKY_SW"){
		return new AllSkySWGenerator(vecArgs, i_algoname, i_section, TZ);
	} else if (algoname == "CLEARSKY_SW"){
		return new ClearSkySWGenerator(vecArgs, i_algoname, i_section, TZ);
	} else if (algoname == "WINDCOMPONENTS"){
		return new WindComponents(vecArgs, i_algoname, i_section, TZ);
	} else if (algoname == "RADCOMPONENTS"){ 
		return new RadiationComponents(vecArgs, i_algoname, i_section, TZ);
	} else if (algoname == "ESOLIP"){
		return new ESOLIPGenerator(vecArgs, i_algoname, i_section, TZ);
	} else if (algoname == "PRECSPLITTING"){
		return new PrecSplitting(vecArgs, i_algoname, i_section, TZ);
	} else if (algoname == "METEOINDEX"){
		return new MeteoIndex(vecArgs, i_algoname, i_section, TZ);
	} else if (algoname == "PPHASE"){
		throw IOException("The generator algorithm '"+algoname+"' has been replaced by the PRECSPLITTING generator" , AT);
	} else {
		throw IOException("The generator algorithm '"+algoname+"' is not implemented" , AT);
	}
}

GeneratorAlgorithm::GeneratorAlgorithm(const std::vector< std::pair<std::string, std::string> >& vecArgs, const std::string& i_algo, const std::string& i_section, const double& TZ)
                                   : time_restrictions( MeteoProcessor::initTimeRestrictions(vecArgs, "WHEN", i_section+"::"+i_algo+" for station ", TZ) ), excluded_stations( initStationSet(vecArgs, "EXCLUDE") ), kept_stations( initStationSet(vecArgs, "ONLY") ), algo(i_algo), section(i_section) {}

std::set<std::string> GeneratorAlgorithm::initStationSet(const std::vector< std::pair<std::string, std::string> >& vecArgs, const std::string& keyword)
{
	std::set<std::string> results;
	for (size_t ii=0; ii<vecArgs.size(); ii++) {
		if (vecArgs[ii].first==keyword) {
			std::istringstream iss(vecArgs[ii].second);
			std::string word;
			while (iss >> word){
				results.insert(word);
			}
		}
	}

	return results;
}

bool GeneratorAlgorithm::skipStation(const std::string& station_id) const
{
	if (excluded_stations.count(station_id)!=0) return true; //the station is in the excluded list -> skip
	if (kept_stations.empty()) return false; //there are no kept stations -> do not skip

	return (kept_stations.count(station_id)==0);
}

bool GeneratorAlgorithm::skipTimeStep(const Date& dt) const
{
	const size_t nrRestrictions = time_restrictions.size();
	if (nrRestrictions==0) return false; //no restrictions -> always apply
		
	for (size_t ii=0; ii<nrRestrictions; ii++) {
		if (time_restrictions[ii].in(dt)) return false; //within period -> don't skip but apply
		if (time_restrictions[ii] > dt) return true; //only periods after dt are left -> skip this dt
	}
	
	return true; //not within a period -> skip this dt
}

} //namespace

