varying vec2 TexCoord0;
uniform sampler2D tex;
uniform float pts;
uniform float intensity;
uniform sampler2D auxTex;

const float PI = 3.14159265358979323846;
const float EPS = 0.000001;
const int MIN_CELL_COUNT = 2;
const int MAX_CELL_COUNT = 12;
const float RIPPLE_DURATION = 10.0;
const float INIT_PTS_SHIFT = 1.5; // For still images

vec4 getRand(vec2 coord);

float bias(float x, float b)
{
	return x / ((1.0 / b - 2.0) * (1. - x) + 1.0);
}

float gain(float x, float g)
{
	float t = (1.0 / g - 2.0) * (1.0 - (2.0 * x));
	return x < 0.5 ? (x / (t + 1.0)) : (t - x) / (t - 1.0);
}

vec3 degamma(vec3 c)
{
	return pow(c, vec3(2.2));
}
vec3 gamma(vec3 c)
{
	return pow(c, vec3(1.0 / 1.6));
}

void main()
{
	// Original image lies on the xz-plane with y = 0.
	// Aux image with external light lies parallel to the xz-plane above original image.
	// Observer eye is somewhere between the images at both images center axe and
	// looks straight down.

	float surfaceFun = 0.; // Describes water surface
	if (intensity > EPS)
	{
		int cellCount = MIN_CELL_COUNT + int(intensity * MAX_CELL_COUNT);

		// Generate droplets positions
		for (int y = 0; y < cellCount; ++y)
		{
			for (int x = 0; x < cellCount * 2; ++x)
			{
				vec2 cellPos = vec2(float(x),float(y));

				// Random variations for droplet
				vec4 t = getRand(cellPos);

				// Stratify droplet positions
				vec2 pos = cellPos * (1.0 / float(cellCount - 1));
				pos += (0.75 / float(cellCount - 1)) * (t.xy * 2.0 - 1.0);

				// Radius
				vec2 v = TexCoord0 - pos;
				float d = dot(v, v);
				d = pow(d, 0.7);

				float n = (pts + INIT_PTS_SHIFT) * 5.0 * (t.w + 0.2) - t.z * 6.0;
				n *= 0.1 + t.w;
				n = mod(n, RIPPLE_DURATION + t.z * 3.0 + 10.0); // Repeat, plus a pause
				float dmod = d * 99.0;
				float clipFun = dmod < (2.0 * PI * n) ? 1.0 : 0.0; // Clip to 0 after end
				float fadeFun = max(1. - (n / RIPPLE_DURATION), 0.0); // Entirely fade out by now
				float decayFun = fadeFun * dmod / (2.0 * PI * n); // Leading edge stronger and decay

				float rippleFun = sin(dmod - (2.0 * PI * n) - PI * 0.5);

				rippleFun = rippleFun * 0.5 + 0.5; // Bias needs [0, 1]
				rippleFun = bias(rippleFun, 0.6); // Shape the ripple profile


				rippleFun = (decayFun * rippleFun) / (dmod + 1.1) * clipFun;

				surfaceFun += rippleFun * 100.0 * (0.5 + t.w);
			}
		}
	}

	vec3 norm = vec3(dFdx(surfaceFun), 17., dFdy(surfaceFun)); // Normal vector at water surface
	norm = normalize(norm);

	// Calculate reflection of aux image from water surface
	vec3 eye = vec3(0., 1., 0.); // Eye vector. It looks along y-axis
	vec3 reflectVec = reflect(-eye, norm);
	vec3 reflectColor = degamma(texture2D(auxTex, TexCoord0 + reflectVec.xz).rgb);

	// Calculate input image distortion due to water refraction
	vec2 uv = TexCoord0;
	vec3 refractVec = refract(eye, norm, 2.5);
	uv += refractVec.xz * 0.1;

	// Blur refracted light with reflection and wave shadows
	vec4 texColor = texture2D(tex, uv);
	vec3 color = degamma(texColor.rgb);
	color *= 1.0 - surfaceFun * 0.0125;
	color += reflectColor * 0.3;

	// Some shadows from side light
	vec3 sideLightVec = normalize(vec3(1, 1, 1));
	float dl = max(dot(norm, sideLightVec), 0.0) * 0.7 + 0.3;
	color *= dl;

	color = gamma(color);
	gl_FragColor = vec4(color, texColor.a);
}

vec4 getRand(vec2 coord)
{
	return vec4(
		movavi_rand_(mat2(1.0), vec2(1.0), coord),
		movavi_rand_(mat2(1.0), vec2(1.0), vec2(coord.x + 1.0, coord.y)),
		movavi_rand_(mat2(1.0), vec2(1.0), vec2(coord.x, coord.y + 1.0)),
		movavi_rand_(mat2(1.0), vec2(1.0), coord + 1.0)
	);
}
