precision mediump float;

varying vec4 clipSpace;

uniform sampler2D reflectionTexture;
uniform sampler2D refractionTexture;
uniform sampler2D dudvMap;
uniform sampler2D normalMap;
uniform sampler2D depthMap;

uniform float uReflectivity;
uniform float uDUDVOffset;
uniform vec3 uLightColor;
uniform vec4 uTint;

uniform float uShine;
uniform float uShineDamper;
uniform float uDistortionStrength;

uniform float uNearPlane;
uniform float uFarPlane;

varying vec2 uv;
varying vec3 toCameraVector;
varying vec3 fromLightVector;

void main() {
  vec2 ndc = (clipSpace.xy / clipSpace.w) / 2.0 + 0.5;
  vec2 refractTexCoords = vec2(ndc.x, ndc.y);
  vec2 reflectTexCoords = vec2(ndc.x, -ndc.y);

  float depth = 2.0 * texture2D(depthMap, refractTexCoords).r - 1.0;
  float floorDistance = 2.0 * uNearPlane * uFarPlane / (uFarPlane + uNearPlane - depth * (uFarPlane - uNearPlane));
  depth = 2.0 * gl_FragCoord.z - 1.0;
  float waterDistance = 2.0 * uNearPlane * uFarPlane / (uFarPlane + uNearPlane - depth * (uFarPlane - uNearPlane));
  float waterDepth = floorDistance - waterDistance;

  vec2 distortedTexCoords = texture2D(dudvMap, vec2(uv.x + uDUDVOffset, uv.y)).rg * 0.1;
  distortedTexCoords = uv + vec2(distortedTexCoords.x, distortedTexCoords.y + uDUDVOffset);

  // Between -1 and 1
  float depthDistortionBlend = clamp(waterDepth / 20.0, 0.0, 1.0);
  vec2 totalDistortion = (texture2D(dudvMap, distortedTexCoords).rg * 2.0 - 1.0) * uDistortionStrength * depthDistortionBlend;

  refractTexCoords += totalDistortion;
  reflectTexCoords += totalDistortion;

  refractTexCoords = clamp(refractTexCoords, 0.001, 0.999);
  reflectTexCoords.x = clamp(reflectTexCoords.x, 0.001, 0.999);
  reflectTexCoords.y = clamp(reflectTexCoords.y, -0.999, -0.001);

  reflectTexCoords.y = 1.0 + reflectTexCoords.y;

  vec4 reflectColor = texture2D(reflectionTexture, reflectTexCoords);
  vec4 refractColor = texture2D(refractionTexture, refractTexCoords);

  vec3 viewVector = normalize(toCameraVector);
  float refractiveFactor = dot(viewVector, vec3(0.0, 1.0, 0.0));
  refractiveFactor = pow(refractiveFactor, uReflectivity);

  vec4 normalMapColor = texture2D(normalMap, distortedTexCoords);
  vec3 normal = vec3(normalMapColor.r * 2.0 - 1.0, normalMapColor.b, normalMapColor.g * 2.0 - 1.0);
  normal = normalize(normal);

  float depthAlpha = clamp(waterDepth / 5.0, 0.0, 1.0);

  vec3 reflectedLight = reflect(normalize(fromLightVector), normal);
  float specular = max(dot(reflectedLight, viewVector), 0.0);
  specular = pow(specular, uShineDamper);
  vec3 specularHighlights = uLightColor * specular * uShine * depthAlpha;

  gl_FragColor = mix(reflectColor, refractColor, refractiveFactor);
  gl_FragColor = mix(gl_FragColor, vec4(uTint.xyz, 1.0), uTint.w) + vec4(specularHighlights, 0.0);
  gl_FragColor.a = depthAlpha;
}
