#ifndef ATMOSPHERE_PRECOMPUTE
#define ATMOSPHERE_PRECOMPUTE

#include "AtmosphereFunctions.hlsl"


struct AtmosphereVSOut {
	float4 vertex	: SV_POSITION;
	float2 uv		: TEXCOORD0;
};

void MainVS (
	in float4 InPosition : POSITION,
	in float2 InTexCoord : TEXCOORD0,
	out AtmosphereVSOut Out
	)
{
	Out.vertex = TransformObjectToHClip(InPosition.xyz);
	Out.uv = InTexCoord.xy;
}


#define TRANSMITTANCE_INTEGRAL_SAMPLES 25
#define INSCATTER_INTEGRAL_SAMPLES 25




/////////////////////////////////////////////////////////////////////////////////////////

void PS_Loss(AtmosphereVSOut IN, out float4 OutColor : SV_Target0)
{
	float2 texc = IN.uv.xy;

	float sin_e			= max(-1.0, min(1.0, 1.0 - 2.0*(texc.y*NUM_ELEVATIONS - 0.5) / (NUM_ELEVATIONS - 1.0)));
	float cos_e			= sqrt(1 - sin_e * sin_e);
	float zLayer		= floor(texc.x * NUM_DISTANCES * NUM_ALTITUDES / NUM_ALTITUDES);
	float altTexc		= (fmod(texc.x*NUM_DISTANCES * NUM_ALTITUDES, NUM_ALTITUDES) - 0.5) / (NUM_ALTITUDES - 1.0);
	float viewAltKm		= TexcToAltKm(altTexc, minOutputAltKm, maxOutputAltKm);
	float spaceDistKm	= GetDistanceToSpace(sin_e, viewAltKm);
	//float zPosition		= pow(float(zLayer) / (float(NUM_DISTANCES) - 1.0), 2.0);
	/*float dist_km = zPosition*maxDistanceKm;
	if (zLayer == NUM_DISTANCES - 1)
		dist_km = 12000.0;*/
	//float maxd = min(spaceDistKm, dist_km);
	//float stepLengthKm = maxd / TRANSMITTANCE_INTEGRAL_SAMPLES;
	//float prevDist_km = 0.0;
	
	/*float dens_factor = 0;
	float haze_factor = 0;
	float dens_i = GetMieFactorAtAltitude(viewAltKm);
	float haze_i = GetHazeFactorAtAltitude(viewAltKm);*/
	float prevDist_km = 0.0;
	float4 previous_loss = float4(1.0, 1.0, 1.0, 1.0);
	//for (int i = 1; i <= TRANSMITTANCE_INTEGRAL_SAMPLES; ++i)
	for (int i = 0; i < NUM_DISTANCES; ++i)
	{
		if (i <= zLayer)
		{
			float zPosition = pow(float(i) / (float(NUM_DISTANCES) - 1.0), 2.0);
			float dist_km = zPosition*maxDistanceKm;
			if (i == NUM_DISTANCES - 1)
				dist_km = 12000.0;
			float maxd = min(spaceDistKm, dist_km);
			float mind = min(spaceDistKm, prevDist_km);
			float dist = 0.5*(mind + maxd);
			float stepLengthKm = max(0.0, maxd - mind);
			float y = planetRadiusKm + viewAltKm + dist*sin_e;
			float x = dist*cos_e;
			float r = sqrt(x*x + y*y);
			float alt_km = r - planetRadiusKm;

			// lookups is: dens_factor,ozone_factor,haze_factor;
			float dens_texc = saturate((alt_km / maxDensityAltKm*(DENSITY_TABLE_SIZE - 1.0) + 0.5) / DENSITY_TABLE_SIZE);
			float4 lookups = SAMPLE_TEXTURE2D(density_texture, sampler_density_texture, float2(dens_texc, .5));

			float dens_factor = lookups.x;
			float ozone_factor = lookups.y;
			float haze_factor = GetHazeFactorAtAltitude(alt_km);
			float3 extinction = dens_factor*rayleigh + haze_factor*hazeMie + ozone*ozone_factor;
			float4 loss;
			loss.rgb = exp(-extinction*stepLengthKm);
			loss.a = (loss.r + loss.g + loss.b) / 3.0;

			loss *= previous_loss;
			prevDist_km = dist_km;
			previous_loss = loss;
		}
		/*float dist = float(i) * stepLengthKm;
		float y = planetRadiusKm + viewAltKm + dist*sin_e;
		float x = dist*cos_e;
		float r = sqrt(x*x + y*y);
		float alt_km = r - planetRadiusKm;

		float dens_j = GetMieFactorAtAltitude(alt_km);
		float haze_j = GetHazeFactorAtAltitude(alt_km);

		dens_factor += (dens_i + dens_j) * 0.5;
		haze_factor += (haze_i + haze_j) * 0.5;

		dens_i = dens_j;
		haze_i = haze_j;
		*/
	}
	/*float3 extinction = dens_factor*rayleigh + haze_factor*hazeMie;
	float4 loss;
	loss.rgb = exp(-extinction*stepLengthKm);
	loss.a = (loss.r + loss.g + loss.b) / 3.0;*/

	OutColor = previous_loss;
}

void PS_Insc(AtmosphereVSOut IN, out float4 OutColor : SV_Target0)
{
	float2 texc = IN.uv.xy;

	float sin_e			= max(-1.0, min(1.0, 1.0 - 2.0*(texc.y*NUM_ELEVATIONS - 0.5) / (NUM_ELEVATIONS - 1.0)));
	float cos_e			= sqrt(1 - sin_e * sin_e);
	float zLayer		= floor(texc.x * NUM_DISTANCES * NUM_ALTITUDES / NUM_ALTITUDES);
	texc.x				-= zLayer / NUM_DISTANCES;
	float altTexc		= (texc.x*NUM_ALTITUDES*NUM_DISTANCES - 0.5) / (NUM_ALTITUDES - 1.0);
	float viewAltKm		= TexcToAltKm(altTexc, minOutputAltKm, maxOutputAltKm);
	float spaceDistKm	= GetDistanceToSpace(sin_e, viewAltKm);
	float prevDist_km = 0.0;
	float4 previous_insc = float4(0.0, 0.0, 0.0, 0.0);
	float3 mie_factor = float3(1.0, 1.0, 1.0);
	for (int i = 1; i < NUM_DISTANCES; ++i)
	{
		if (i <= zLayer)
		{
			float2 lossTexc = texc;
			lossTexc.x += i / NUM_DISTANCES;
			float3 previous_loss = SAMPLE_TEXTURE2D(loss_texture, sampler_loss_texture, lossTexc);

			float zPosition = pow(float(i) / (float(NUM_DISTANCES) - 1.0), 2.0);
			float dist_km = zPosition * maxDistanceKm;
			if (i == NUM_DISTANCES - 1)
				dist_km = 1000.0;

			float maxd = min(spaceDistKm, dist_km);
			float mind = min(spaceDistKm, prevDist_km);
			float dist = 0.5*(mind + maxd);
			float stepLengthKm = max(0.0, maxd - mind);
			float y = planetRadiusKm + viewAltKm + dist*sin_e;
			float x = dist * cos_e;
			float r = sqrt(x*x + y*y);
			float alt_km = r - planetRadiusKm;

			// lookups is: dens_factor,ozone_factor,haze_factor;
			float dens_texc = (alt_km / maxDensityAltKm * (DENSITY_TABLE_SIZE - 1.0) + 0.5) / DENSITY_TABLE_SIZE;
			float4 lookups = SAMPLE_TEXTURE2D(density_texture, sampler_density_texture, float2(dens_texc, 0.5));
			float dens_factor = lookups.x;
			float ozone_factor = lookups.y;
			//float dens_factor = GetMieFactorAtAltitude(alt_km);
			float haze_factor = GetHazeFactorAtAltitude(alt_km);

			float4 light = float4(sunIrradiance, 1.0)*(
				GetSunlightFactor(alt_km, x2m_SunDirSize.xyz)
				+ float4(moonIrradianceFactor, 1.0) * GetSunlightFactor(alt_km, /*directionToMoon*/x2m_MoonDirSize)
				);
			light.rgb *= RAYLEIGH_BETA_FACTOR;
			float4 insc = light;

			float3 extinction = dens_factor*rayleigh + haze_factor*hazeMie;
			//float3 total_ext		=extinction+ozone*ozone_factor;
			float3 loss;
			loss = exp(-extinction*stepLengthKm);
			insc.rgb *= float3(1.0, 1.0, 1.0) - loss;
			mie_factor *= exp(-insc.w*stepLengthKm*haze_factor*hazeMie);

			insc.rgb *= previous_loss.rgb;
			insc.rgb += previous_insc.rgb;

			insc.w = saturate((1.0 - mie_factor.x) / (1.0 - previous_loss.x + 0.0001f));
			//float3 ctexc = (float3(idx) + float3(0.5, 0.5, 0.5)) / float3(targetSize.xyz);
			float3 insc_colour = lerp((float3(0, 0, 0)), lerp(horizon, zenith, saturate(sin_e)), 1.0 - previous_loss.r);
			float4 store_insc = lerp(insc, float4(insc_colour, insc.a), blendToColours);
			prevDist_km = dist_km;
			previous_insc = store_insc;
		
		}
		
	}

	OutColor = previous_insc;
}

void PS_Skyl(AtmosphereVSOut IN, out float4 OutColor : SV_Target0)
{
	float2 texc = IN.uv.xy;

	float sin_e			= max(-1.0, min(1.0, 1.0 - 2.0*(texc.y*NUM_ELEVATIONS - 0.5) / (NUM_ELEVATIONS - 1.0)));
	float cos_e			= sqrt(1 - sin_e * sin_e);
	float zLayer		= floor(texc.x * NUM_DISTANCES * NUM_ALTITUDES / NUM_ALTITUDES);
	texc.x				-= zLayer / NUM_DISTANCES;
	float altTexc		= (texc.x*NUM_ALTITUDES*NUM_DISTANCES - 0.5) / (NUM_ALTITUDES - 1.0);
	float viewAltKm		= TexcToAltKm(altTexc, minOutputAltKm, maxOutputAltKm);
	float spaceDistKm	= GetDistanceToSpace(sin_e, viewAltKm);
	
	float prevDist_km = 0.0;
	float4 previous_skyl = float4(0.0, 0.0, 0.0, 1.0);
	for (int i = 0; i < NUM_DISTANCES; ++i)
	{
		if (i <= zLayer)
		{
			float2 lossTexc = texc;
			lossTexc.x += i / NUM_DISTANCES;
			float3 previous_loss = SAMPLE_TEXTURE2D(loss_texture, sampler_loss_texture, lossTexc);

			float zPosition = pow(float(i) / (float(NUM_DISTANCES) - 1.0), 2.0);
			float dist_km = zPosition*maxDistanceKm;
			if (i == NUM_DISTANCES - 1)
				dist_km = 1000.0;

			float4 skyl = Skyl(TEXTURE2D_ARGS(insc_texture, sampler_insc_texture)
							, TEXTURE2D_ARGS(density_texture, sampler_density_texture)
							, TEXTURE2D_ARGS(blackbody_texture, sampler_blackbody_texture)
							,previous_loss
							,previous_skyl
							,spaceDistKm
							,viewAltKm
							,dist_km
							,prevDist_km
							,sin_e
							,cos_e);
			
			prevDist_km = dist_km;
			previous_skyl = skyl;
		}
		
	}

	OutColor = previous_skyl;
}

TEXTURE2D(_MainTex);		SAMPLER(sampler_MainTex);
void PS_Fade3DTo2D(AtmosphereVSOut IN, out float4 OutColor : SV_Target0)
{
	float zLayer = floor(IN.uv.x * NUM_DISTANCES);
	float2 texc = float2(zLayer / NUM_DISTANCES + altitudeTexCoord, IN.uv.y);
	OutColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, texc);
}



#endif // ATMOSPHERE_PRECOMPUTE