#version 120
const int nLightCount = 2;

//Amount of population considered 'half full'
const float basePopulation = 7.0;

const float distortDist = 4.0 / 2048.0;
const float distortScale = 128.0;

const float mergeNoise = 2.0;
const float mergeScale = 2.0;
const float mergeA = 5.0, mergeB = 7.0;
const float extremeness = 1.8;

const float baseSpec = 0.5;

uniform sampler2D surfaces[3], cities, masks, diffNoise;
uniform float population, time[2];

varying vec3 normal, binormal, tangent;
varying vec3 npos, vertPaint, origo;
varying vec2 uv;

vec3 light[nLightCount];
float dist[nLightCount];

vec2 nightLights(vec3 incidentLight, float devLevel) {
	float brightness = clamp(max(incidentLight.r, max(incidentLight.g, incidentLight.b)) * 0.9, 0.0, 1.0);
	brightness /= 0.4;

	float popDensity = population / (population + basePopulation);
	float city = texture2D(cities, uv).r;
	float buildup = clamp((popDensity - (1.0 - devLevel)) * 2.0, 0.0, 1.0);
	
	float lightFactor = sqrt(max(popDensity, 0.4));
	
	return vec2(lightFactor * max(1.0 - brightness, 0.0) * buildup, max(buildup - 0.5, 0.0) * 2.0) * city;
}


float hash2D(vec2 n) { 
	return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453);
}

float hash3D(float n ) {
	return fract(sin(n)*753.5453123);
}

float noise2D(vec2 n) {
	const vec2 d = vec2(0.0, 1.0);
	vec2 b = floor(n), f = smoothstep(vec2(0.0), vec2(1.0), fract(n));
	return mix(mix(hash2D(b), hash2D(b + d.yx), f.x), mix(hash2D(b + d.xy), hash2D(b + d.yy), f.x), f.y);
}

float noise3D(vec3 x )
{
    vec3 p = floor(x);
    vec3 f = fract(x);
    f = f*f*(3.0-2.0*f);
	
    float n = p.x + p.y*157.0 + 113.0*p.z;
    return mix(mix(mix( hash3D(n+  0.0), hash3D(n+  1.0),f.x),
                   mix( hash3D(n+157.0), hash3D(n+158.0),f.x),f.y),
               mix(mix( hash3D(n+113.0), hash3D(n+114.0),f.x),
                   mix( hash3D(n+270.0), hash3D(n+271.0),f.x),f.y),f.z);
}

float fbm3D(vec3 n) {
	float total = 0.0, amplitude = 1.0;
	for (int i = 0; i < 5; i++) {
		total += noise3D(n) * amplitude;
		n += n;
		amplitude *= 0.5;
	}
	return total;
}

float fbm2D(vec2 n) {
	float total = 0.0, amplitude = 1.0;
	for (int i = 0; i < 5; i++) {
		total += noise2D(n) * amplitude;
		n += n;
		amplitude *= 0.5;
	}
	return total;
}

float extremify(float x, float treme) {
	if(x < 0.5)
		x = pow(x, treme);
	else
		x = 1.0 - pow(1.0 - x, treme);
	return x;
}

void main() {
	vec3 color = gl_FrontMaterial.diffuse.rgb;
	
	vec2 mask = texture2D(masks, uv).rg;
	float distort = mask.r;
	vec2 lookup = uv;
	lookup.x *= 36.0;
	
	mask = texture2D(masks, lookup).rg;
	
	vec2 noise = texture2D(diffNoise, uv * distortScale).rg;
	lookup += distort * (noise - vec2(0.5)) * distortDist * vec2(1.0,0.5);
	
	vec3 surfA = texture2D(surfaces[0], lookup).rgb;
	vec3 surfB = texture2D(surfaces[1], lookup).rgb;
	vec3 surfC = texture2D(surfaces[2], lookup).rgb;
	
	float surf = lookup.x + (texture2D(diffNoise, lookup * mergeScale).b * mergeNoise - mergeNoise/2.0);
	surf = mod(surf, mergeA) / mergeA;
	surf = extremify(abs(surf - 0.5) * 2.0, extremeness);
	float surf2 = lookup.x + (texture2D(diffNoise, lookup * mergeScale).b * mergeNoise - mergeNoise/2.0);
	surf2 = mod(surf2, mergeB) / mergeB;
	surf2 = extremify(abs(surf2 - 0.5) * 2.0, extremeness);
	
	vec3 diffuseSamp = mix(surfA, surfB, surf);
	diffuseSamp = mix(diffuseSamp, surfC, surf2);
	
	float gloss = 0.5;
	float shininess = gl_FrontMaterial.shininess * (gloss + baseSpec);

	vec3 n = normalize(normal);
	vec3 v = normalize(npos);
	
	if(nLightCount > 0) {
		const int i = 0;
		light[i] = gl_LightSource[i].position.xyz + npos;
		dist[i] = length(light[i]);
		light[i] = light[i] / dist[i];
	}
	if(nLightCount > 1) {
		const int i = 1;
		light[i] = gl_LightSource[i].position.xyz + npos;
		dist[i] = length(light[i]);
		light[i] = light[i] / dist[i];
	}
	
	vec3 ambient = gl_LightModel.ambient.rgb * gl_FrontMaterial.ambient.rgb;
	vec3 diffuse = vec3(0.0);
	vec3 specular = vec3(0.0);
	
	float shadow = abs(mod(uv.x + time[0] + 0.0, 1.0/8.0) * 8.0 - 0.5) * 2.0;
	shadow = smoothstep(0.7,0.8, shadow);
	
	if(nLightCount > 0) {
		const int i = 0;
		float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
		float intensity = max(0.0, dot(n, light[i])) * falloff * (1.0 - shadow * 0.9);
		
		diffuse += gl_LightSource[i].diffuse.rgb * intensity;
		
		vec3 r = -reflect(light[i], n);
		specular += gl_LightSource[i].specular.rgb * (pow(max(0.0, dot(r, v)), shininess) * intensity);
	}
	if(nLightCount > 1) {
		const int i = 1;
		float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
		float intensity = max(0.0, dot(n, light[i])) * falloff * (1.0 - shadow * 0.9);
		
		diffuse += gl_LightSource[i].diffuse.rgb * intensity;
		
		vec3 r = -reflect(light[i], n);
		specular += gl_LightSource[i].specular.rgb * (pow(max(0.0, dot(r, v)), shininess) * intensity);
	}
	specular *= gl_FrontMaterial.specular.rgb * (0.5 + gloss - 0.5);
	
	vec2 cityState = nightLights(diffuse, mask.g);
	/*
	float turbulence = noise2D(vec2(uv.x * 100.0 -time * 15.0, uv.y * 6.0));
	color = ((diffuse + ambient) * color * mix(diffuseSamp.rgb, vec3(0.5), cityState.y)) + specular + cityState.x * vec3(1.0,0.9,0.55);
	float clouds = max(0.0, fbm2D(vec2(uv.x * 125.0 + time * 17.0, uv.y * 5.0 + time * 1.0) + turbulence) * (fbm2D(vec2(uv.x*400 - time * 16.0, uv.y * 18.0 - time * 6.0)) * 0.5 + 0.5) - 0.75);
	clouds *= min(1.0, uv.x * (1.0 - uv.x) * 25.0);
	vec3 atmosphere = pow((1.0 - dot(v, n)), 4.0) * vec3(0.5,0.75,1.0) * vertPaint.r * 2.0;
	shadow = (1.0 - shadow * 0.8);
	
	
	
	
	
	gl_FragColor.rgb = mix(atmosphere + clouds, mix(color, color * 0.25, clouds), vertPaint.r);// * gl_LightSource[0].diffuse.rgb;
	gl_FragColor.a = mix(min(1.0, clouds + atmosphere.r), 1.0, vertPaint.r);
	*/
	
	
	float clearSky = clamp(abs(mod(uv.x * 5.0 + 0.5, 1.0) * 2.0 - 1.0) * 3.0 - 0.1, 0.0, 1.0);
	float turbulence = noise2D(vec2(uv.x * 300.0 -time[1] * 50.0, uv.y * 25.0)) * (1.0 - clearSky * 0.8);
	color = ((diffuse + ambient) * color * mix(diffuseSamp.rgb, vec3(0.5), cityState.y)) + specular + cityState.x * vec3(1.0,0.9,0.55);
	float clouds = min(1.0, (1.0 - max(0.0, fbm2D(vec2(uv.x * 125.0 + time[1] * 25, uv.y * 5.0 + time[1] * 5.0) + turbulence))) * 2.0);// * (fbm2D(vec2(uv.x*400 - time[1] * 16.0, uv.y * 18.0 - time[1] * 6.0)) * 0.5 + 0.5) - 0.75);

	// calculate a mask that makes clear skies over deserts and hides the UV seam that generates bad noise.
	clouds *= clearSky;
	
	//<0.5,0.7,1.0> <1.0,0.5,0.1>
	vec3 atmosphere = pow((1.0 - dot(v, n)), 2.0) * (mix(vec3(0.5,0.7,1.0), vec3(1.0,0.5,0.1), shadow) * (1.0 - pow(shadow, 2.0))) * (1.0 - vertPaint.r) * 2.0;
	shadow = (1.0 - shadow * 0.75);
	
	gl_FragColor.rgb = mix(mix(atmosphere, gl_LightSource[0].diffuse.rgb, clouds) * shadow, mix(color, color * 0.75, clouds), vertPaint.r);
	gl_FragColor.a = mix(min(1.0, clouds + atmosphere.r * shadow), 1.0, vertPaint.r);
	
}
