#version 130
#extension GL_EXT_texture_array : enable

uniform sampler2D noiseTexture;
uniform sampler2DArray textureArray;
uniform sampler2DArray normalArray;
uniform sampler2DArray detailArray;

uniform vec3 playerPos;
uniform vec3 sunPosition;
uniform float blockAlpha;

// Vertex shader input
in vec3 fWorldPos;
in vec3 fNormal;
in vec3 fMaterial;
in vec3 fAlpha;
in float fIntensity;

const float textureScale = 0.095f;  // 0.095 good, 0.125 old
const float detailScale = 0.7f;
float PI = 3.14159265358979323846264;

float getAlphaWeight(float alpha, float noise)
{	
	alpha = smoothstep(0, 0.9, alpha + noise - 0.45);
	return pow(alpha, 3);
}

vec3 getFragmentNormal(float index, float weightXY, float weightXZ, float weightYZ)
{
	vec2 coordXY = fWorldPos.xy * textureScale;
	vec2 coordXZ = fWorldPos.xz * textureScale;	
	vec2 coordYZ = fWorldPos.yz * textureScale;	

	vec4 bump1 = texture2DArray(normalArray, vec3(coordXY.xy, index));	
	vec4 bump2 = texture2DArray(normalArray, vec3(coordXZ.xy, index));
	vec4 bump3 = texture2DArray(normalArray, vec3(coordYZ.xy, index));

	vec3 bumpNormal1 = bump1.r * vec3(1, 0, 0) + bump1.g * vec3(0, 1, 0) + bump1.b * vec3(0, 0, 1);
	vec3 bumpNormal2 = bump2.r * vec3(0, 0, 1) + bump2.g * vec3(1, 0, 0) + bump2.b * vec3(0, 1, 0);
	vec3 bumpNormal3 = bump3.r * vec3(0, 1, 0) + bump3.g * vec3(0, 0, 1) + bump3.b * vec3(1, 0, 0);
	
	return weightXY * bumpNormal1 + weightXZ * bumpNormal2 + weightYZ * bumpNormal3;
}

vec4 sampleWeighed(sampler2DArray texture, float index, vec3 coord, vec3 weight, float scale)
{
	float wXY = weight.z;
	float wXZ = weight.y;
	float wYZ = weight.x;

	vec2 coordXY = coord.xy * scale;
	vec2 coordXZ = coord.xz * scale;
	vec2 coordYZ = coord.yz * scale;
	
	vec4 colorXY = texture2DArray(texture, vec3(coordXY.xy, index));
	vec4 colorXZ = texture2DArray(texture, vec3(coordXZ.xy, index));
	vec4 colorYZ = texture2DArray(texture, vec3(coordYZ.xy, index));
	
	return colorXY * wXY + colorXZ * wXZ + colorYZ * wYZ;
}

vec4 sampleWeighed2D(sampler2D texture, vec3 coord, vec3 weight, float scale)
{
	float wXY = weight.z;
	float wXZ = weight.y;
	float wYZ = weight.x;

	vec2 coordXY = coord.xy * scale;
	vec2 coordXZ = coord.xz * scale;
	vec2 coordYZ = coord.yz * scale;
	
	vec4 colorXY = texture2D(texture, coordXY.xy);
	vec4 colorXZ = texture2D(texture, coordXZ.xy);
	vec4 colorYZ = texture2D(texture, coordYZ.xy);
	
	return colorXY * wXY + colorXZ * wXZ + colorYZ * wYZ;
}

vec4 desaturate(vec4 color)
{
	float lum = (0.299 * color.r) + (0.587 * color.g) + (0.114 * color.b);
	return vec4(lum, lum, lum, 1.0);
}

void main()
{
	float mXY, mXZ, mYZ; 
	float sXY, sXZ, sYZ;

	// Raise each component of normal vector to 4th power.
	//vec3 blend = abs(normalize(fNormal));
	vec3 blend = clamp(abs(normalize(fNormal)) - 0.5,0.0,1.0);	
	blend *= blend;
	blend *= blend;
		
	// Normalize result by dividing by the sum of its components.
	blend /= dot(blend, vec3(1.0, 1.0, 1.0));
	
	mXY = blend.z;
	mXZ = blend.y;
	mYZ = blend.x;
	
	sXY = fNormal.z;
	sXZ = fNormal.y;
	sYZ = fNormal.x;
	
	/* vec2 coordXY = fWorldPos.xy * textureScale;
	vec2 coordXZ = fWorldPos.xz * textureScale;	
	vec2 coordYZ = fWorldPos.yz * textureScale;	 */
	
	vec4 blendedColor = sampleWeighed(textureArray, fMaterial.r, fWorldPos, blend, textureScale);
	vec4 blendedColor2 = sampleWeighed(textureArray, fMaterial.g, fWorldPos, blend, textureScale);
	vec4 blendedColor3 = sampleWeighed(textureArray, fMaterial.b, fWorldPos, blend, textureScale);

	//vec4 d_blendedColor = sampleWeighed(textureArray, fMaterial.r, fWorldPos, blend, 0.5);
	//vec4 d_blendedColor2 = sampleWeighed(textureArray, fMaterial.g, fWorldPos, blend, 0.5);
	//vec4 d_blendedColor3 = sampleWeighed(textureArray, fMaterial.b, fWorldPos, blend, 0.5); 

/*
	blendedColor.rgb *= desaturate(d_blendedColor).rgb;
	blendedColor2.rgb *= desaturate(d_blendedColor2).rgb; 
	blendedColor3.rgb *= desaturate(d_blendedColor3).rgb;   */
	
	 
	// Blending of 2 fake materials (for now)
	vec3 alph = fAlpha.rgb;
	
	float noise = 0.0;
	float s = 0.4;
	noise += mXY * texture2D(noiseTexture, fWorldPos.xy * s).r;
	noise += mXZ * texture2D(noiseTexture, fWorldPos.xz * s).r;
	noise += mYZ * texture2D(noiseTexture, fWorldPos.yz * s).r;
	
	alph = fAlpha.rgb;
	alph.r = getAlphaWeight(alph.r, noise);	
	alph.g = getAlphaWeight(alph.g, noise);	
	alph.b = getAlphaWeight(alph.b, noise);	
	
	alph /= (alph.r + alph.g + alph.b);
				
	vec3 finalColor;
	
	finalColor += alph.r * blendedColor.rgb;
	finalColor += alph.g * blendedColor2.rgb;	
	finalColor += alph.b * blendedColor3.rgb; 
	
	vec4 detailColor = sampleWeighed(detailArray, fMaterial.r, fWorldPos, blend, detailScale);
	//finalColor = detailColor.rgb;
	finalColor *= detailColor.r * 2;
	//finalColor /= 1.6;

	//----- Basic light in absence of openGL lighting, looks weird and ugly 
	//----- but at least highlights some parts of terrain
	vec4 baseColor = vec4(0,0,0,0);
	vec3 norm, lightDir;
	vec4 diffuse, ambient, globalAmbient;
	float NdotL;	// modified normal dot light dir
	float ONdotL;	// original normal dot light dir

	lightDir = normalize(sunPosition - fWorldPos);
	// calculate the original normal vs sun, to not light surfaces facing away from the sun
	ONdotL = max(dot(fNormal, lightDir),0);	// puts ONdotL between 0..1
	ONdotL = min(1, ONdotL+0.5);	// should put ONdotL at either 0 or 1
	
	vec3 fragDetailNormal = getFragmentNormal(fMaterial.r, sXY, sXZ, sYZ) * alph.r +
		                    getFragmentNormal(fMaterial.g, sXY, sXZ, sYZ) * alph.g +
		                    getFragmentNormal(fMaterial.b, sXY, sXZ, sYZ) * alph.b;
		                     
	vec3 fragNormal = mix(fNormal.xyz, normalize(fragDetailNormal), ONdotL);
		
	NdotL = max(dot(fragNormal, lightDir), 0.0);
	
	diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;	  // change to uniform array?
	//ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;	  // change to uniform array?
	//globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient; // change to uniform array?

	vec4 dayAmbient = vec4(0.3, 0.3, 0.3, 1.0);
	vec4 nightAmbient = vec4(0.05, 0.05, 0.05, 1.0);
	ambient = mix(nightAmbient, dayAmbient, fIntensity);
	
	baseColor = fIntensity * (NdotL * diffuse) + ambient; 

	finalColor *= baseColor.rgb;
	
	//finalColor = vec3(fIntensity, fIntensity, fIntensity) + vec3(0.0,0.0, 0.0);
		
    gl_FragColor = vec4(finalColor.rgb, 1); // FOG    
}
