#include "DepressionWaterCalc.h"

void DepressionWaterCalc::DepressionWater(Inputs* input, CompactRagged* beC, int MapPixel_ID, int DataFolder_ID, int timeStep, map<string, function<void(Inputs*, int)> >& functors)
{
	//Storage_WaterPondedWaterMax_m (m) is adjusted from HydroPlusConfig.xml input of WaterDepressionStorage_mm (mm) by Ratio_m_to_mm
	beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_WaterPondedWaterMax_m") = beC->by_key(MapPixel_ID, DataFolder_ID, "WaterDepressionStorage_mm") * Ratio_m_to_mm;

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

	//calculateWaterDepEvap functor computes Evaporation_WaterPondedWater_m as cover area depth in OpenWaterEvaporationCalc::calculateWaterDepEvapStatistical function
	functors["calculateWaterDepEvap"](input, timeStep);

	//If Evaporation_WaterPondedWater_m (m) > Storage_WaterPondedWater_m (m) then set to Storage_WaterPondedWater_m (m)
	if (beC->by_key(MapPixel_ID, DataFolder_ID, "Evaporation_WaterPondedWater_m") > beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_WaterPondedWater_m")) {
		//Evaporation_WaterPondedWater_m (m) is set to Storage_WaterPondedWater_m (m)
		beC->by_key(MapPixel_ID, DataFolder_ID, "Evaporation_WaterPondedWater_m") = beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_WaterPondedWater_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_WaterPondedWater_m") = 0.0;
	}

	//Storage_WaterPondedWater_m (m) as cover area depth increased by Flux_to_WaterArea_Rain_SnowMelt_m (m) minus Evaporation_WaterPondedWater_m (m) as cover area depths
	beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_WaterPondedWater_m") = beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_WaterPondedWater_m") +
		beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_to_WaterArea_Rain_SnowMelt_m") - beC->by_key(MapPixel_ID, DataFolder_ID, "Evaporation_WaterPondedWater_m");

	//If Storage_WaterPondedWater_m < Storage_WaterPondedWaterMax_m then Runoff_Water_to_Outlet_m equal zero
	if (beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_WaterPondedWater_m") < beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_WaterPondedWaterMax_m")) {
		beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Water_to_Outlet_m") = 0.0;
	}
	//Else If Storage_WaterPondedWater_m >= Storage_WaterPondedWaterMax_m then Runoff_Water_to_Outlet_m may exceed zero
	else if (beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_WaterPondedWater_m") >= beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_WaterPondedWaterMax_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

		//Runoff_Water_to_Outlet_m (m) is cover area depth computed as Flux_to_WaterArea_Rain_SnowMelt_m (m) minus available storage (Storage_WaterPondedWaterMax_m - Storage_WaterPondedWater_PriorTS_m) 
		//Note: ... minus Evaporation_WaterPondedWater_m (m)
		beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Water_to_Outlet_m") = (beC->by_key(MapPixel_ID, DataFolder_ID, "Flux_to_WaterArea_Rain_SnowMelt_m") -
			(beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_WaterPondedWaterMax_m") - Storage_WaterPondedWater_PriorTS_m) - beC->by_key(MapPixel_ID, DataFolder_ID, "Evaporation_WaterPondedWater_m"));

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

	//Control for non zero values
	beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Water_to_Outlet_m") = Inputs::forceZeroIfLessThanOrAlmostEqualZero(beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Water_to_Outlet_m"), Epsilon_Tolerance_1E_negative15);
	beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_WaterPondedWater_m") = Inputs::forceZeroIfLessThanOrAlmostEqualZero(beC->by_key(MapPixel_ID, DataFolder_ID, "Storage_WaterPondedWater_m"), Epsilon_Tolerance_1E_negative15);

	//If GI is operational in HydroPlusConfig.xml then implement the transfer of water between BulkArea and GI; WaterCover_noTreeCanopy_frac > 0 allows for division
	//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" && beC->by_key(MapPixel_ID, DataFolder_ID, "WaterCover_noTreeCanopy_frac") > 0) {
		double Runoff_Water_to_Outlet_BAtoGI_m = 0;

		//Runoff_Water_to_Outlet_BAtoGI_m (m) is BulkArea Runoff_Water_to_Outlet_m (m) going to GI area, 
		//Note: Runoff_Water_to_Outlet_m is multiplied below by WaterCover_noTreeCanopy_frac to temporarily convert it from cover area depth to folder area depth
		Runoff_Water_to_Outlet_BAtoGI_m = (beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Water_to_Outlet_m") * beC->by_key(MapPixel_ID, DataFolder_ID, "WaterCover_noTreeCanopy_frac")) *
			input->RepoDict["BulkAreaWater_DrainingToGI_frac"];

		//Note: Division of variables by fractional area below is to adjust depths from entire folder area to one analysis area
		//Rationale: 1. variables arrive as depths for the entire folder area
		//Rationale: 2. multiplying these depths by the folder area gives a volume
		//Rationale: 3. dividing these depths by the analysis area is equivalent to multiplying by the folder area and dividing by the analysis area, getting depth for analysis area
		//Note: Division is equivalent to multiplying by ratio of folder area to analysis area to convert from /folder_area to /analysis_area

		//Runoff_Water_to_Outlet_m (m) reduced by Runoff_Water_to_Outlet_BAtoGI_m (m) going to GI
		//Note: Runoff_Water_to_Outlet_m (m) is cover area depth, and Runoff_Water_to_Outlet_BAtoGI_m is folder depth, so ...
		//Note: ... Runoff_Water_to_Outlet_BAtoGI_m (m) is divided by WaterCover_noTreeCanopy_frac to temporarily convert it from folder area depth to cover area depth
		beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Water_to_Outlet_m") = beC->by_key(MapPixel_ID, DataFolder_ID, "Runoff_Water_to_Outlet_m") -	(Runoff_Water_to_Outlet_BAtoGI_m / beC->by_key(MapPixel_ID, DataFolder_ID, "WaterCover_noTreeCanopy_frac"));

		//Runoff_Water_BAtoGI_m (m) is runon to GI, as folder depth, is Runoff_Water_to_Outlet_BAtoGI_m (m), a folder depth
		input->RepoDict["Runoff_Water_BAtoGI_m"] = Runoff_Water_to_Outlet_BAtoGI_m;
		//Runoff_Water_BAtoGI_m3 (m3) is runon to GI, as volume, by taking product of Runoff_Water_BAtoGI_m (m) and Area_BulkArea_m2 (m2)
		input->RepoDict["Runoff_Water_BAtoGI_m3"] = input->RepoDict["Runoff_Water_BAtoGI_m"] * input->RepoDict["Area_BulkArea_m2"];
	}
}



