#version 140

#define MAX_LIGHTS 16

uniform mat4 u_modelMatrix;

uniform int u_lightCount;
uniform float u_shadowTexelStep;
uniform float u_lightRadius[MAX_LIGHTS];
uniform vec3 u_lightOrigin[MAX_LIGHTS];
uniform vec4 u_shadowRect[MAX_LIGHTS];

in vec4 attr_Position;
in vec4 attr_TexCoord;

out vec2 texCoord;

const mat3 cubicTransformations[6] = mat3[6] (
	mat3(
		0, 0, -1,
		0, -1, 0,
		-1, 0, 0
	),
	mat3(
		0, 0, 1,
		0, -1, 0,
		1, 0, 0
	),
	mat3(
		1, 0, 0,
		0, 0, -1,
		0, 1, 0
	),
	mat3(
		1, 0, 0,
		0, 0, 1,
		0, -1, 0
	),
	mat3(
		1, 0, 0,
		0, -1, 0,
		0, 0, -1
	),
	mat3(
		-1, 0, 0,
		0, -1, 0,
		0, 0, 1
	)
);

const float clipEps = 0e-2;
const vec4 ClipPlanes[4] = vec4[4] (
	vec4(1, 0, -1, clipEps),
	vec4(-1, 0, -1, clipEps),
	vec4(0, 1, -1, clipEps),
	vec4(0, -1, -1, clipEps)
);
 
// I suspect that the right way is to cook the projection matrices for each page on CPU and pass as uniforms to avoid this mess
vec2 ShadowAtlasForVector(vec3 v, int lightNo, int targetFace) {
	vec2 shadow2d = v.xy*.5 + vec2(-v.z)*.5;
	float pageSize = u_shadowRect[lightNo].w + u_shadowTexelStep;
	vec2 pageOrigin = u_shadowRect[lightNo].xy;
	pageOrigin.x += pageSize * targetFace;
	pageOrigin = pageOrigin * 2 - 1;
	shadow2d *= u_shadowRect[lightNo].w*2 + u_shadowTexelStep*5e-3; // FIXME rasterization rules workaround
	shadow2d.xy += -v.z * pageOrigin;
	return vec2(shadow2d);
}

void main() {
	gl_Position = u_modelMatrix * attr_Position;
	//gl_Position = gl_Vertex;
	texCoord = (attr_TexCoord).st;

	int lightNo = gl_InstanceID / 6;
	vec3 inLightSpace = gl_Position.xyz - u_lightOrigin[lightNo];
    int j = gl_InstanceID % 6;

		vec4 clip;
		vec4 clipCount = vec4(0);
			vec4 inCubeFaceSpace = vec4(cubicTransformations[j] * inLightSpace, 1);
			for(int k=0; k<4; k++) {
				clip[k] = dot(inCubeFaceSpace, ClipPlanes[k]);
				clipCount[k] += float(clip[k] < 0);
			}
			gl_Position.xy = ShadowAtlasForVector(inCubeFaceSpace.xyz, lightNo, j);
            gl_Position.z = (-inCubeFaceSpace.z - 2*u_lightRadius[lightNo]);
            gl_Position.w = -inCubeFaceSpace.z;
			// clip the triangle to the atlas page 
			gl_ClipDistance[0] = clip.x;
			gl_ClipDistance[1] = clip.y;
			gl_ClipDistance[2] = clip.z;
			gl_ClipDistance[3] = clip.w;
}