#version 120

uniform sampler2D texture;

uniform float nodeScale;
uniform vec3 colors[2];

varying vec2 uv;
varying vec3 normal, binormal, tangent, light, light2;
varying vec3 pos, center;
varying float dist, dist2;

//planetRim is the (normal dot view) value corresponding to the edge of the planet on the atmosphere's model
const float planetRim = 0.215;
const float innerFade = 0.4;
const float innerFadeMax = 0.9;
const float atmosExaggerateDist = 250.0;
const float outerFade = 0.1;

//The thickness of atmosphere that must be present before any haze is visible
const float hazeThreshold = 0.03;
//The brightness multiplier of the haze
const float hazeBrightFactor = 7.0;
//Fraction of haze that occurs beneath the clouds
const float lowHaze = 0.7;
const float hiHaze = 1.0 - lowHaze;

//How much cover is 100% cloud cover
const float shadowDarkness = 0.75;

const float shadowDist = 0.025;

vec3 hazeColor(float NdL, vec3 lightCol) {
	float depth = pow(max(0.8 - NdL, 0.0), 2.0);
	float lum = max(lightCol.r, max(lightCol.g, lightCol.b));
	vec3 col = mix(colors[0], colors[1], depth);
	col /= max(col.r, max(col.g, col.b));
	return col  * lum * pow(max((0.8 + NdL) * 0.5, 0.0), 1.3);
}

vec4 alphaBlend(vec4 dest, vec4 src) {
	float alpha = src.a + (dest.a * (1.0 - src.a));
	return vec4( ((src.rgb * src.a) + (dest.rgb * dest.a * (1.0 - src.a))) / alpha, alpha);
}

void main() {
	vec3 normMap = texture2D(texture, uv).rgb - vec3(0.5);
	float opacity = max((length(normMap) - 0.25) / 0.25, 0.0);
	vec3 n = normalize(normal);
	
	vec3 cloudNorm = n * normMap.z;
	cloudNorm += normalize(binormal) * normMap.x;
	cloudNorm += normalize(tangent) * normMap.y;
	cloudNorm = normalize(cloudNorm);
	
	float hazeBrightness = hazeBrightFactor / nodeScale;
	
	vec3 p = -pos;
	//Apply a correction to the position to correct for the vertex structure
	p += nodeScale * (length(normal) - 1.0) * n;
	
	vec3 v = normalize(p);
	vec3 c = normalize(center);
	float cosTheta = -dot(c,v);
	float theta = acos(cosTheta);
	
	float dToCenter = length(center);
	float dToAtmos = length(p);
	float dToFarAtmos;
	float dToSurface;
	{
		float k = sin(theta) * dToCenter;
		float e = sqrt(nodeScale*nodeScale - k*k);
		dToSurface = sqrt(dToCenter*dToCenter - k*k) - e;
		
		e = sqrt(pow(nodeScale*1.015,2.0) - k*k);
		dToFarAtmos = dToAtmos + e*2.0;
	}

	vec3 l = normalize(light);
	float ndv = n.z;
	if(ndv < 0.0)
		discard;
	float ndl = dot(n,l);
	
	//Calculate shadow cover
	float shadow = 0.0;
	if(ndl > 0.0 && ndv > planetRim) {
		vec2 off;
		off.x = asin(dot(binormal,l)) * shadowDist / 6.283;
		off.y = asin(dot(tangent,l)) * shadowDist / 6.283;
		
		float thickness = max((length(texture2D(texture, uv + off.xy).rgb - vec3(0.5))-0.25)/0.25, 0.0) * sqrt(ndl);
		shadow = thickness * shadowDarkness;
	}
	
	float lightFactor = 1.0 / (1.0 + (gl_LightSource[0].quadraticAttenuation * dist * dist));
	vec3 r = normalize(-reflect(l, n));
	float rDv = dot(r,v);
	
	float nDl = dot(cloudNorm,light);
    float nl = max(0.0,  nDl);
	
	//Calculate atmospheric haze
	//Haze increases in brightness with depth through the atmosphere
	//The color changes as light is preferentially scattered away by frequency
	
	vec3 haze = vec3(0.0);
	//if(ndv >= planetRim) {
	//	float hazeFactor = max(dToSurface - dToAtmos - hazeThreshold, 0.0) * hazeBrightness;
	//	haze = hazeColor(ndl, gl_LightSource[0].diffuse.rgb * lightFactor) * hazeFactor * 0.0;
	//}
	float hazeLum = max(haze.r, max(haze.g, haze.b));
	
	vec3 rimHaze = vec3(0.0);
	float inFade = innerFade;
	if(ndv <= inFade) {
		float hazeFactor = max(dToFarAtmos - dToAtmos, 0.0) * hazeBrightness * 0.5 * nl;
		hazeFactor *= smoothstep(inFade,planetRim,ndv) * smoothstep(outerFade,inFade,ndv);
		rimHaze = hazeColor(ndl, gl_LightSource[0].diffuse.rgb * lightFactor) * hazeFactor;
	}
	float rimLum = max(rimHaze.r, max(rimHaze.g, rimHaze.b));

    vec3 diffuse = gl_LightSource[0].diffuse.rgb * nl * lightFactor;
	diffuse += gl_LightModel.ambient.rgb * gl_FrontMaterial.ambient.rgb;

    vec3 specular = vec3(0.0);
	{ //Light 0
		rDv = dot(normalize(-reflect(l, cloudNorm)),v);
		if(rDv > 0.0) {
			specular += gl_LightSource[0].specular.rgb * pow(rDv, gl_FrontMaterial.shininess) * lightFactor * pow(nl, 0.4);
		}
	}
	specular *= gl_FrontMaterial.specular.rgb;;
	
	vec4 clouds = vec4(diffuse + specular, opacity);
	if(ndv < planetRim)
		clouds.a = 0.0;
	
	//vec4 result = vec4(hazeFactor,0.0,0.0,1.0);
	vec4 result = vec4(0.0,0.0,0.0,shadow);
	if(hazeLum > 0.0)
		result = alphaBlend(result, vec4(haze / hazeLum, hazeLum * lowHaze));
	if(clouds.a > 0.0)
		result = alphaBlend(result, clouds);
	if(hazeLum > 0.0)
		result = alphaBlend(result, vec4(haze / hazeLum, hazeLum * hiHaze));
	if(rimLum > 0.0)
		result = alphaBlend(result, vec4(rimHaze / rimLum, rimLum));
	//result = vec4(shadow,0.0,0.0,1.0);
	
	gl_FragColor = result;
}
