#version 150

layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;

#include <shaders/materials/noise/noise3d.glsl>

uniform mat4 mModel;
uniform vec4 vModelview[3];
uniform mat4 mProjection;
uniform vec4 vNearFarPlane;

uniform float vTweenFactor;
uniform float vTweenScale;
uniform float vNoiseShift;

uniform float vDistortionBaseDistance;
uniform float vDistortionRange;

uniform sampler2D s_texture_depthbuffer;
uniform sampler2D s_texture_colorbuffer;

// from vertex shader
#define HAS_TESSELATION_SHADER
#ifdef HAS_TESSELATION_SHADER
in vec3 teNorm[];
in vec3 teWorldNorm[];
in vec3 teLocalPos[];
in vec4 teWorldPos[];
in float teVtxTweenFactor[];
#define vNorm teNorm
#define vWorldNorm teWorldNorm
#define vLocalPos teLocalPos
#define vWorldPos teWorldPos
#define vVtxTweenFactor teVtxTweenFactor
#else
in vec3 vNorm[];
in vec3 vWorldNorm[];
in vec3 vLocalPos[];
in vec4 vWorldPos[];
in float vVtxTweenFactor[];
#endif

out vec3 vGSNorm;
out vec3 vGSWorldNorm;
out vec3 vGSLocalPos;
out vec4 vGSWorldPos;
out vec2 vGSTexCoord;
out float vGSBrightness;

uniform float vTweenBase; // = 0.45;		// 0 is center(0.0) 1 is base position

//float orient2d(vec2 a, const Point2D& b, const Point2D& c)
//{
//	return (b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x);
//}

float linearizeDepth(float d)
{
	return vNearFarPlane.z / (vNearFarPlane.y + vNearFarPlane.x - d * vNearFarPlane.w);
}

float sample_average_depth(vec4 pPosProj)
{
	int px, py;
	float v = 0.0;

	pPosProj.xy = pPosProj.xy / pPosProj.w;
	pPosProj.xy = pPosProj.xy * vec2(0.5, 0.5) + vec2(0.5, 0.5);

	const float sample_div = 1.0 / 25.0;
	//for (px = -2; px <=2; px += 1)
	{
	//	for (py = -2; py < 2; py += 1)
		{
			//v += texture(s_texture_depthbuffer, pPosProj.xy + vec2(px, py)).r;
			const float r = 0.001;
			v += texture(s_texture_depthbuffer, pPosProj.xy).r;
			v += texture(s_texture_depthbuffer, pPosProj.xy + r).r;
			v += texture(s_texture_depthbuffer, pPosProj.xy - r).r;
			v += texture(s_texture_depthbuffer, pPosProj.xy + vec2(r, 0.0)).r;
			v += texture(s_texture_depthbuffer, pPosProj.xy - vec2(r, 0.0)).r;
			v += texture(s_texture_depthbuffer, pPosProj.xy + vec2(0.0, r)).r;
			v += texture(s_texture_depthbuffer, pPosProj.xy - vec2(0.0, r)).r;
			v += texture(s_texture_depthbuffer, pPosProj.xy + vec2(r, -r)).r;
			v += texture(s_texture_depthbuffer, pPosProj.xy - vec2(r, -r)).r;
			
		}
	}

	//v = v * sample_div;
	v = v / 9.0;
	return linearizeDepth(v);
}

void main()
{

	float shift[3];

	shift[0] = 0.0;
	shift[1] = 0.0;
	shift[2] = 0.0;

	vec3 center = (teLocalPos[0].xyz + teLocalPos[1].xyz + teLocalPos[2].xyz) / 3.0;

	// sample depth from the center

	float distortion_scale[3];
	distortion_scale[0] = 0.0;
	distortion_scale[1] = 0.0;
	distortion_scale[2] = 0.0;

	float distortion_scale_total = 0.0;

	for(int i=0; i<3; i++)
	{
		vec4 localPos = vec4(vLocalPos[i], 1.0);
		vec4 vPos;
		vPos.x = dot(vModelview[0], localPos);
		vPos.y = dot(vModelview[1], localPos);
		vPos.z = dot(vModelview[2], localPos);
		vPos.w = 1.0;

		vec4 pPosProj = mProjection * vPos;
		float depth_at_center = sample_average_depth(pPosProj);

		// distortion is focused on a plane specified by range
		float diff = abs(depth_at_center - vDistortionBaseDistance);
		if (diff < vDistortionRange)
			distortion_scale[i] = 2.0 * (1.0 - (diff / vDistortionRange));
		else
			distortion_scale[i] = 0.0;

		distortion_scale[i] *= 0.8 + 0.3 * sin(vNoiseShift + pPosProj.z * 2.0);
		distortion_scale_total += distortion_scale[i];
	}

	// bring all the distortions together a bit
	distortion_scale_total = distortion_scale_total / 3.0;
	distortion_scale[0] = mix(distortion_scale[0], distortion_scale_total, 0.9);
	distortion_scale[1] = mix(distortion_scale[1], distortion_scale_total, 0.9);
	distortion_scale[2] = mix(distortion_scale[2], distortion_scale_total, 0.9);

	//

	// TODO: now all verts fall uniquely
	//if (vVtxTweenFactor[0] > 0.0)
	{
		for (int i = 0; i < 3; i++)
		{
			vec3 noise_pos = center.xyz + vLocalPos[i].xyz;
			float noise1 = snoise((noise_pos + vNoiseShift * 0.0131) * vTweenScale);
			float noise2 = snoise((noise_pos + vNoiseShift * 0.0031) * vTweenScale * 1.30137 + 2.0113);
			float n = vTweenBase * (1.0 + noise1 + 0.5 * noise2) + vTweenFactor * 0.01;// -vVtxTweenFactor[0] * 0.05 - 5.0;

			shift[i] = n;
		}

		// pull all the verts together
		//shift[0] = (shift[0] + shift[1] + shift[2]) / 3.0;
		//shift[1] = mix(shift[0], shift[1], 0.1);
		//shift[2] = mix(shift[0], shift[2], 0.1);
	}

	// dont emit really small primitives
	if (distortion_scale_total < 0.01)
		return;

	vec3 n_world = cross(vWorldPos[1].xyz - vWorldPos[0].xyz, vWorldPos[2].xyz - vWorldPos[0].xyz);
	n_world = normalize(n_world);
	
	for (int i = 0; i < gl_in.length(); i++)
	{

		vGSNorm = vNorm[0];
		// NOTE: somewhere we have fucked up normal direction?
		vGSWorldNorm = -n_world;
		//vGSWorldNorm = -vWorldNorm[0];
		vGSLocalPos = vLocalPos[i];
		vGSWorldPos = vWorldPos[i];

		//vec3 localPos = vLocalPos[i];// +scale * vec3(0.0, 1.0, 0.0) *shift[i] * 0.1;
		float scale = distortion_scale[i];
		//vec3 localPos = mix(vLocalPos[i], center, 1.0 - (scale * (1.0 + shift[i] * 0.1))) + scale * n_world * shift[i] * 0.01;
		vec3 localPos = mix(vLocalPos[i], center, 1.0 - scale) + (1.0 - scale) * n_world * shift[i] * 0.01;

		// now we can do the transformations. NOTE: maybe move some to vertex shader if things go too slow...
		vec4 vPos;
		vPos.x = dot(vModelview[0], vec4(localPos, 1.0));
		vPos.y = dot(vModelview[1], vec4(localPos, 1.0));
		vPos.z = dot(vModelview[2], vec4(localPos, 1.0));
		vPos.w = 1.0;
		vGSWorldPos = mModel * vec4(localPos, 1.0);

		vGSBrightness = scale + abs(shift[i]);

		// texture coords to sample background (undeformed)
		{
			vec4 p = vec4(vLocalPos[i], 1.0);
			vec4 mp;
			mp.x = dot(vModelview[0], p);
			mp.y = dot(vModelview[1], p);
			mp.z = dot(vModelview[2], p);
			mp.w = 1.0;

			vec4 pPosProj = mProjection * mp;
			pPosProj.xy = pPosProj.xy / pPosProj.w;
			pPosProj.xy = pPosProj.xy * vec2(0.5, 0.5) + vec2(0.5, 0.5);
			vGSTexCoord = pPosProj.xy;
		}

		gl_Position = mProjection * vPos;

		EmitVertex();
	}

	EndPrimitive();
}

