#version 150

in vec3 vsPosition;
in vec3 vsNormal;
in vec4 vsColor;
in vec2 vsTextureUV;
 
// Our outputs the next shader (fragment) in the chain
out vec3 fsWorldPosition;
out vec3 fsNormal;
out vec4 fsColor;
out vec2 fsTextureUV;
out float fsDotLight;

out vec3 fsDirectionVS;

out vec4 shadowCoordinates[4];
uniform mat4 uSunCameraView;
uniform mat4 uSunCameraProjection[4];

uniform vec4 uShadowNormalOffset = vec4(0.05);
const mat4 bias = mat4(
	vec4(0.5, 0.0, 0.0, 0.5),
	vec4(0.0, 0.5, 0.0, 0.5),
	vec4(0.0, 0.0, 0.5, 0.5),
	vec4(0.0, 0.0, 0.0, 1.0)
);

uniform sampler2D sAlbedoTexture;
uniform mat4 uObjectToProjection;
uniform mat4 uObjectToWorld;
uniform mat4 uWorldToView;
uniform mat4 uWorldToProjection;
uniform vec4 uWaterSettings; //x = time, y = height, z = length, w = speed
uniform mat4 uNoiseSettings; //4 vectors of: x = scale, y = speed, z = offsetDirX, w = offsetDirY
uniform vec4 uSunLightDirection = vec4(-0.447766751, 0.373415232, 0.812444329, 0.422158539);  //Expected to be a unit vector and in world space already.

#define PI 3.1415926535897932384626433832795

float GetHeight(float x, float z)
{
	float time = uWaterSettings.x;
	float height = uWaterSettings.y;
	float wavelength = uWaterSettings.z;

	return sin(z * 0.1 - time) * height / 100.0;
}

void main(void)
{	//Using https://catlikecoding.com/unity/tutorials/flow/waves/ as reference.
	float time = uWaterSettings.x;
	float height = uWaterSettings.y / 100.0f; //aka Amplitude //divide by 100 because hardcoded scale....
	float waveLength = uWaterSettings.z;
	float waveSpeed = uWaterSettings.w;

	vec3 worldPosition = (vec4(vsPosition, 1.0f) * uObjectToWorld).xyz;

	float k = 2.0 * PI / waveLength;
	float f = k * (worldPosition.z - waveSpeed * time); //F?

	//world.xz * scale + offset * speed * time
	float waveHeight = 0.0;
	waveHeight += textureLod(sAlbedoTexture, worldPosition.xz * uNoiseSettings[0].x + uNoiseSettings[0].zw + uNoiseSettings[0].y * time, 0).r;
	// waveHeight += textureLod(sAlbedoTexture, worldPosition.xz * uNoiseSettings[1].x + uNoiseSettings[1].zw + uNoiseSettings[1].y * time, 0).r * 0.75;
	// waveHeight += textureLod(sAlbedoTexture, worldPosition.xz * uNoiseSettings[2].x + uNoiseSettings[2].zw + uNoiseSettings[2].y * time, 0).r * 0.25;
	// waveHeight = pow(waveHeight / 2.0, 5.0) * height;

	float a = textureLod(sAlbedoTexture, worldPosition.xz * uNoiseSettings[1].x + uNoiseSettings[1].zw + uNoiseSettings[1].y * time, 0).r;
	float b = textureLod(sAlbedoTexture, worldPosition.xz * uNoiseSettings[2].x + uNoiseSettings[2].zw + uNoiseSettings[2].y * time, 0).r;
	waveHeight += min(a, b);
	waveHeight = pow(waveHeight / 2.0, 5.0) * height;
	waveHeight = min(waveHeight, 0.02);

	waveHeight *= 1.0 - pow(smoothstep(0.7, 1.0, length(vsPosition.x)), 1.0);
	waveHeight *= 1.0 - pow(smoothstep(0.7, 1.0, length(vsPosition.z)), 1.0);

	vec3 position = vsPosition + vec3(0.0, waveHeight, 0.0);
	gl_Position = vec4(position, 1.0) * uObjectToProjection;

	//Bad normal, not used...
	fsNormal = vec3(normalize(vec4(vsNormal, 0.0) * uObjectToWorld));

	//vec3 tangent = normalize(vec3(1.0, k * height * cos(f), 0.0));
	//fsNormal = normalize(vec4(-tangent.y, tangent.x, 0.0f, 0.0) * uObjectToWorld).xyz;
	//fsNormal = normalize(vec3(1.0, ));

	fsWorldPosition = (vec4(position, 1.0) * uObjectToWorld).xyz;
	fsDotLight = clamp(dot(fsNormal, vec3(uSunLightDirection)), 0.0, 1.0);
	fsColor = vsColor;
	fsTextureUV = vsTextureUV;

	mat4 uWorldToSunView = uSunCameraView;
	mat4 modelSunView = uObjectToWorld * uWorldToSunView;
	mat4 uSunViewToSunProjection[4] = uSunCameraProjection;

	vec4 vpos = vec4(fsWorldPosition, 1.0) * uWorldToView;
	fsDirectionVS = -vpos.xyz;

	for (int i = 0; i < 4; i++)
	{
		float shadowOffset = max(0.0, dot(fsNormal, uSunLightDirection.xyz)) * uShadowNormalOffset[i];
		vec4 positionOffset = vec4(vsPosition + fsNormal * shadowOffset, 1.0f) * modelSunView;
		shadowCoordinates[i] = positionOffset * uSunViewToSunProjection[i] * bias;
	}
}
