#include "DepressionImperviousCalc.h"
#include "../Evaporation/OpenWaterEvaporationCalc.h"

void DepressionImperviousCalc::DepressionImpervious(Inputs *input, CompactRagged* beC, int MapPixel_ID, int DataFolder_ID, int timeStep, map<string, function<void(Inputs*, int)> >& functors)
{

	//PerviousCover_frac is fraction of folder area in pervious cover, used to check (1-DCIA) based partitioning of flow from impervious area to pervious area
	double PerviousCover_frac = 0.0;
	PerviousCover_frac = beC->by_key(MapPixel_ID, DataFolder_ID, "PerviousCover_frac");

	//Storage_ImperviousPondedWater_PriorTS_m (m) is Storage_ImperviousPondedWater_m (m)
	double Storage_ImperviousPondedWater_PriorTS_m = beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_m");

	//calculateImpDepEvap functor computes Evaporation_ImperviousPondedWater_m in OpenWaterEvaporationCalc::calculateImpDepEvapStatistical function
	functors["calculateImpDepEvap"](input, timeStep);

	//If Evaporation_ImperviousPondedWater_m (m) > Storage_ImperviousPondedWater_m (m) then set to Storage_ImperviousPondedWater_m (m)
	if (beC->by_key(MapPixel_ID, DataFolder_ID, "Evaporation_ImperviousPondedWater_m") > beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_m")) {
		//Evaporation_ImperviousPondedWater_m (m) is set to Storage_ImperviousPondedWater_m (m)
		beC->by_key(MapPixel_ID, DataFolder_ID, "Evaporation_ImperviousPondedWater_m") = beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_m");
	}

	//If Storage_Snow_noCanopy_m (m) > 0, there is snow on ground and liquid evaporation from storage is zero for this timestep
	if (beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_Snow_noCanopy_m") > 0.0) {
		beC->by_key(MapPixel_ID, DataFolder_ID, "Evaporation_ImperviousPondedWater_m") = 0.0;
	}

	//Storage_ImperviousPondedWater_m (m) increased by Flux_to_ImperviousArea_Rain_SnowMelt_Irrigation_m (m) minus Evaporation_ImperviousPondedWater_m (m), scaled to cover type area
	beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_m") = beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_m") +
		beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_to_ImperviousArea_Rain_SnowMelt_Irrigation_m") - beC->by_key(MapPixel_ID, DataFolder_ID, "Evaporation_ImperviousPondedWater_m");

	//If Storage_ImperviousPondedWater_m < Storage_ImperviousPondedWater_Max_m then Runoff_Impervious_to_Outlet_m and Flux_Impervious_to_Pervious_m equal zero
	if (beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_m") < beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_Max_m")) {
			beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Impervious_to_Outlet_m") = 0.0;
			beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_Impervious_to_Pervious_m") = 0.0;
	}
	//Else If Storage_ImperviousPondedWater_m >= Storage_ImperviousPondedWater_Max_m then Runoff_Impervious_to_Outlet_m and Flux_Impervious_to_Pervious_m may exceed zero
	else if(beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_m") >= beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_Max_m")) {

		//Note: Multiplication of variables by fractional area below is to adjust depths from one analysis area to entire folder area
		//Rationale: 1. variables arrive as depths for an analysis area (e.g., pervious area)
		//Rationale: 2. multiplying these depths by their analysis area gives a volume, which is conserved when transfered between different areas
		//Rationale: 3. multiplying these depths by their fractional area is equivalent to multiplying by their analysis area and dividing by the folder area, getting depth for folder area
		//Note: Multiplication is equivalent to multiplying by ratio of analysis area to folder area to convert from /analysis_area to /folder_area
		//Note: Consider refactor to use volumes and avoid moving between variable depth for cover area and folder area, as done with Flux_Impervious_to_Pervious_m

		//Flux_Impervious_to_Pervious_m3 (m3) is volume for impervious cover draining to pervious, ImperviousCover_DrainsToPervious_frac 
		//Note: Impervious area water available to drain to pervious is Flux_to_ImperviousArea_Rain_SnowMelt_Irrigation_m minus water newly ponded ...
		//Note: ... determined by Storage_ImperviousPondedWater_Max_m - Storage_ImperviousPondedWater_PriorTS_m, and ...
		//Note: ... water evaporated, Evaporation_ImperviousPondedWater_m
		//Note: ImperviousCover_DrainsToPervious_frac (fraction) is product of (1-DCIA_frac) and ImperviousCover_frac, including ...
		//Note: ... water and IC under TC
		beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_Impervious_to_Pervious_m3") = (beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_to_ImperviousArea_Rain_SnowMelt_Irrigation_m") -
			(beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_Max_m") - Storage_ImperviousPondedWater_PriorTS_m) - beC->by_key(MapPixel_ID, DataFolder_ID, "Evaporation_ImperviousPondedWater_m")) * beC->by_key(MapPixel_ID, DataFolder_ID, "Area_m2") * beC->by_key(MapPixel_ID, DataFolder_ID, "ImperviousCover_DrainsToPervious_frac");

		//Flux_Impervious_to_Pervious_m (m) is adjusted to folder depth by division of Flux_Impervious_to_Pervious_m3 (m3) by Area_m2 (m2)
		beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_Impervious_to_Pervious_m") = beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_Impervious_to_Pervious_m3") / beC->by_key(MapPixel_ID, DataFolder_ID, "Area_m2");

		//Runoff_Impervious_to_Outlet_m3 (m3) is volume for impervious cover draining to outlet, ImperviousCover_DrainsToOutlet_frac 
		//Note: Impervious area water available to drain to outlet is Flux_to_ImperviousArea_Rain_SnowMelt_Irrigation_m minus water newly ponded ... 
		//Note: ... determined by Storage_ImperviousPondedWater_Max_m - Storage_ImperviousPondedWater_PriorTS_m, and ...
		//Note: ... water evaporated, Evaporation_ImperviousPondedWater_m
		//Note: ImperviousCover_DrainsToOutlet_frac (fraction) is product of DCIA_frac and ImperviousCover_frac, ...
		//Note: ... including water and IC under TC
		beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Impervious_to_Outlet_m3") = (beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_to_ImperviousArea_Rain_SnowMelt_Irrigation_m") -
			(beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_Max_m") - Storage_ImperviousPondedWater_PriorTS_m) - beC->by_key(MapPixel_ID, DataFolder_ID, "Evaporation_ImperviousPondedWater_m")) * beC->by_key(MapPixel_ID, DataFolder_ID, "Area_m2") * beC->by_key(MapPixel_ID, DataFolder_ID, "ImperviousCover_DrainsToOutlet_frac");

		//Runoff_Impervious_to_Outlet_m (m) is adjusted to folder depth by division of Runoff_Impervious_to_Outlet_m3 (m3) by Area_m2 (m2)
		beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Impervious_to_Outlet_m") = beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Impervious_to_Outlet_m3") / beC->by_key(MapPixel_ID, DataFolder_ID, "Area_m2");

		//Storage_ImperviousPondedWater_m (m) set to Storage_ImperviousPondedWater_Max_m (m), filled
		beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_m") = beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_Max_m");
	}

	//Control for non zero values
	beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Impervious_to_Outlet_m3") = Inputs::forceZeroIfLessThanOrAlmostEqualZero(beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Impervious_to_Outlet_m3"), Epsilon_Tolerance_1E_negative15);
	beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_Impervious_to_Pervious_m3") = Inputs::forceZeroIfLessThanOrAlmostEqualZero(beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_Impervious_to_Pervious_m3"), Epsilon_Tolerance_1E_negative15);
	beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Impervious_to_Outlet_m") = Inputs::forceZeroIfLessThanOrAlmostEqualZero(beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Impervious_to_Outlet_m"), Epsilon_Tolerance_1E_negative15);
	beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_Impervious_to_Pervious_m") = Inputs::forceZeroIfLessThanOrAlmostEqualZero(beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_Impervious_to_Pervious_m"), Epsilon_Tolerance_1E_negative15);
	beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_m") = Inputs::forceZeroIfLessThanOrAlmostEqualZero(beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_ImperviousPondedWater_m"), Epsilon_Tolerance_1E_negative15);

	//If GI is operational in HydroPlusConfig.xml then implement the transfer of water between BulkArea and GI
	//Note: input->RepoDict[] variables are used for transfer between BulkArea and GI area folders
	if (input->RepoDict["Flag_GI_Simulated"] == 1 && beC->by_key_str(MapPixel_ID, DataFolder_ID, "Type") == "BulkArea") {
		double Runoff_Impervious_to_Outlet_BAtoGI_m = 0;
		double Flux_Impervious_to_Pervious_BAtoGI_m = 0;

		//Runoff_Impervious_to_Outlet_BAtoGI_m (m) is BulkArea Runoff_Impervious_to_Outlet_m (m) going to GI area
		//Note: Runoff_Impervious_to_Outlet_m (m) adjusted above to folder BulkArea depth from cover depth with ImperviousCover_DrainsToOutlet_frac
		Runoff_Impervious_to_Outlet_BAtoGI_m = beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Impervious_to_Outlet_m") * input->RepoDict["BulkAreaImpervious_DrainingToGI_frac"];
		//Flux_Impervious_to_Pervious_BAtoGI_m (m) is BulkArea Flux_Impervious_to_Pervious_m (m) going to GI area; 
		//Note: Flux_Impervious_to_Pervious_m (m) adjusted above to folder BulkArea depth from cover depth with ImperviousCover_DrainsToPervious_frac
		Flux_Impervious_to_Pervious_BAtoGI_m = beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_Impervious_to_Pervious_m") * input->RepoDict["BulkAreaImpervious_DrainingToGI_frac"];

		//Runoff_Impervious_to_Outlet_m (m) reduced by Runoff_Impervious_to_Outlet_BAtoGI_m (m) going to GI
		beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Impervious_to_Outlet_m") = beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Impervious_to_Outlet_m") - Runoff_Impervious_to_Outlet_BAtoGI_m;
		//Runoff_Impervious_to_Outlet_m3 (m3) is volume of Runoff_Impervious_to_Outlet_m (m) obtained by multiplication with Area_m2 (m2)
		beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Impervious_to_Outlet_m3") = beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Impervious_to_Outlet_m") * beC->by_key(MapPixel_ID, DataFolder_ID, "Area_m2");

		//Flux_Impervious_to_Pervious_m (m) reduced by Flux_Impervious_to_Pervious_BAtoGI_m (m) going to GI
		beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_Impervious_to_Pervious_m") = beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_Impervious_to_Pervious_m") - Flux_Impervious_to_Pervious_BAtoGI_m;
		//Flux_Impervious_to_Pervious_m3 (m3) is volume of Flux_Impervious_to_Pervious_m (m) obtained by multiplication with Area_m2 (m2)
		beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_Impervious_to_Pervious_m3") = beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_Impervious_to_Pervious_m") * beC->by_key(MapPixel_ID, DataFolder_ID, "Area_m2");

		//Runoff_Impervious_BAtoGI_m (m) is runon to GI, as folder depth, is sum of Runoff_Impervious_to_Outlet_BAtoGI_m and Flux_Impervious_to_Pervious_BAtoGI_m, folder depths
		input->RepoDict["Runoff_Impervious_BAtoGI_m"] = Runoff_Impervious_to_Outlet_BAtoGI_m + Flux_Impervious_to_Pervious_BAtoGI_m;
		//Runoff_Water_BAtoGI_m3 (m3) is runon to GI, as volume, by taking product of Runoff_Impervious_BAtoGI_m (m) and Area_BulkArea_m2 (m2)
		input->RepoDict["Runoff_Impervious_BAtoGI_m3"] = input->RepoDict["Runoff_Impervious_BAtoGI_m"] * input->RepoDict["Area_BulkArea_m2"];
	}
}

