#pragma language glsl3
#ifdef VERTEX

out vec3 EyeDirection_cameraspace;
out vec3 LightDirection_cameraspace;

uniform mat4 projectionMatrix;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;

uniform vec3 lightPosition;
uniform bool debug;

uniform int lightType;

vec4 position(mat4 transform_projection, vec4 vertex_position) {
	if (lightType==3) {
		return transform_projection*vertex_position;
	}
	vec3 Position_cameraspace = (viewMatrix * modelMatrix * vertex_position).xyz; 
	EyeDirection_cameraspace = vec3(0,0,0) - Position_cameraspace;

	vec3 LightPosition_cameraspace = (viewMatrix * vec4(lightPosition,1)).xyz;
	LightDirection_cameraspace = LightPosition_cameraspace + EyeDirection_cameraspace;

	return projectionMatrix*viewMatrix*modelMatrix*vertex_position;
}

#endif

#ifdef PIXEL

in vec3 EyeDirection_cameraspace;

uniform vec3 eyePosition;

uniform sampler2D gbuffer_position;
uniform sampler2D gbuffer_normal;
uniform sampler2D gbuffer_diffuse;

uniform bool debug;

uniform vec3 lightPosition;
uniform vec3 lightColor;
uniform float lightPower;
uniform float lightRange;
uniform float lightAperture;
uniform bool lightShadowed;
uniform vec3 lightDirection;
uniform int lightType;
uniform float lightFalloff;
uniform float lightSoftness;
uniform int lightSamples;
uniform mat4 lightMatrix;
uniform sampler2DShadow lightShadow;
uniform sampler2D lightShadow2;
// uniform float shadowAtlasSize;
// uniform vec4 shadowQuad;
uniform bool drawDepth;
uniform float zNear = 0.1;
uniform float zFar = 10000.0;

const float NEAR = 1;


const mat4 bias = mat4(
	0.5, 0.0, 0.0, 0.0,
	0.0, 0.5, 0.0, 0.0,
	0.0, 0.0, 0.5, 0.0,
	0.5, 0.5, 0.5, 1.0
);
float att (float dist, float radius) {
	return clamp(1.0 - (dist*dist)/(radius*radius), 0.0, 1.0);
}
float signedDistance( vec3 point, vec3 origin, vec3 normal )
{
  return dot(point,normal) - dot(normal,origin);
}  
float map(float value, float min1, float max1, float min2, float max2) {
  return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
}
float depthSample(float linearDepth)
{
    float nonLinearDepth = (zFar + zNear - 2.0 * zNear * zFar / linearDepth) / (zFar - zNear);
    nonLinearDepth = (nonLinearDepth + 1.0) / 2.0;
    return nonLinearDepth;
}

float insideBox(vec2 v, vec2 bottomLeft, vec2 topRight) {
    vec2 s = step(bottomLeft, v) - step(topRight, v);
    return s.x * s.y;   
}

float sampleCube(vec3 v){
  float faceIndex = 0;
  float ma;
  vec2 uv=vec2(1);
  vec3 vAbs = abs(v);

  float sub=1;
  if (vAbs.z >= vAbs.x && vAbs.z >= vAbs.y) {
    faceIndex = v.z < 0.0 ? 5.0 : 4.0;
    ma = 0.5 / vAbs.z;
    uv = vec2(v.z < 0.0 ? -v.x : v.x, -v.y);
  } else if (vAbs.y >= vAbs.x) {
    faceIndex = v.y < 0.0 ? 2.0 : 3.0;
    ma = 0.5 / vAbs.y;
    uv = vec2(v.y < 0.0 ? -v.x: v.x, v.y < 0.0 ? v.z : v.z);
  } else {
    faceIndex = v.x < 0.0 ? 1.0 : 0.0;
    ma = 0.5 / vAbs.x;
    uv = vec2(v.x < 0.0 ? v.z : -v.z, -v.y);
  }
  uv = uv* ma + 0.5;

	vec2 coord = vec2(((1-uv.x)+faceIndex)/6,1-uv.y); 
	
	// float result=0;
	// for (int i=0;i<4;i++){
	// 	float sample = 1-Texel(lightShadow,vec3(coord+ (poissonDisk[i]/700)*(pow(((length(v)-100)/lightRange),1)), (length(v)*0.99)/lightRange ));
	// 	result += sample/4;
	// }
	float result = 1-Texel(lightShadow,vec3(coord, (length(v)*0.99)/lightRange ));
  return result;
}

float linearDepth(float depthSample)
{
    depthSample = 2.0 * depthSample - 1.0;
    float zLinear = 2.0 * zNear * zFar / (zFar + zNear - depthSample * (zFar - zNear));
    return zLinear;
}
vec2 samplePoint;


float random(vec4 seed4){
 float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673));
    return fract(sin(dot_product) * 43758.5453);
}

/////////////////

vec2 VogelDiskSample(int sampleIndex, int samplesCount, float phi)
{
  float GoldenAngle = 2.4f;

  float r = sqrt(sampleIndex + 0.5f) / sqrt(samplesCount);
  float theta = sampleIndex * GoldenAngle + phi;

  float sine=sin(theta);
  float cosine=cos(theta);
  
  return vec2(r * cosine, r * sine);
}
float InterleavedGradientNoise(vec2 position_screen)
{
  vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f);
  return fract(magic.z * fract(dot(position_screen, magic.xy)));
}

float eyeDistance;
float SearchWidth(float uvLightSize, float receiverDistance)
{
	return uvLightSize * (receiverDistance - NEAR) / eyeDistance;
}
float PCF_DirectionalLight(vec3 shadowCoords, sampler2D shadowMap, float uvRadius)
{
	float sum = 0;
	for (int i = 0; i < lightSamples; i++)
	{
		vec2 vogel = VogelDiskSample(i,lightSamples,InterleavedGradientNoise(gl_FragCoord.xy));
		float z = texture(shadowMap, shadowCoords.xy + vogel * uvRadius).r;
		sum += (z < (shadowCoords.z/lightRange - 0.005)) ? 1 : 0;
	}
	return sum / lightSamples;
}
float FindBlockerDistance_DirectionalLight(vec3 shadowCoords, sampler2D shadowMap, float uvLightSize)
{
	int blockers = 0;
	float avgBlockerDistance = 0;
	float searchWidth = SearchWidth(uvLightSize, shadowCoords.z)*2;

	for (int i = 0; i < lightSamples; i++)
	{
		vec2 vogel = VogelDiskSample(i,lightSamples,InterleavedGradientNoise(gl_FragCoord.xy));
		float z = texture(shadowMap, shadowCoords.xy + vogel * searchWidth).r;
		if (z < (shadowCoords.z/lightRange - 0.005))
		{
			blockers++;
			avgBlockerDistance += z;
		}
	}
	if (blockers > 0)
		return avgBlockerDistance / blockers;
	else
		return -1;
}

float PCSS_DirectionalLight(vec3 shadowCoords, sampler2D shadowMap, float uvLightSize)
{
	// blocker search
	float blockerDistance = FindBlockerDistance_DirectionalLight(shadowCoords, shadowMap, uvLightSize);
	if (blockerDistance == -1){
		return 1;		
	}
	float penumbraWidth = (shadowCoords.z/lightRange - blockerDistance) / blockerDistance;
	float uvRadius = penumbraWidth * uvLightSize * NEAR / (shadowCoords.z/lightRange);
	// return uvRadius;
	return  1-PCF_DirectionalLight(shadowCoords, shadowMap,clamp(pow(uvRadius,1.3),0,0.025));
}
//////////////



vec4 effect(vec4 color, sampler2D texturee, vec2 textureCoords, vec2 screen_coords) {

	if (debug) {
		if (color.a==0) {
			discard;
		}else{
      if (drawDepth) {
        return vec4(vec3(linearDepth(Texel(texturee,textureCoords).r)),1);
      }
			return color*vec4(1,1,1,1);
		}
	}

	 samplePoint = screen_coords/love_ScreenSize.xy;
	vec4 diffuse = Texel(gbuffer_diffuse,  samplePoint) ;
	if (diffuse.a==0){
		discard;
	}

	vec4 position = Texel(gbuffer_position,  samplePoint) ;
	vec3 normal   = Texel(gbuffer_normal,    samplePoint).xyz*2-vec3(1);
	
	vec3 n = normalize(normal);

	float lightDistance = distance( lightPosition , position.xyz );

	vec3 l = normalize(lightPosition - position.xyz);
	float cosTheta = pow(clamp(dot(n,  l), 0,1),1);
	float cosPhi = pow(clamp(dot(n,  normalize(lightDirection)), 0,1),1);
	
	// vec3 E = normalize(EyeDirection_cameraspace);
	// vec3 R = reflect(-l,n);

	// float cosAlpha = clamp(dot(E,R), 0,1);	

	// float lit = softLights ? (lightShadowed ?  cosTheta : cosTheta*att(lightDistance,lightRange)) : (lightShadowed ?  cosTheta : cosTheta/ (lightDistance*lightRange));

	// POINT
	if (lightType==1){
		vec4 result = vec4(lightColor *lightPower *att(lightDistance,lightRange) 
      * cosTheta 
      ,1);
		result = lightShadowed ? vec4(vec3(sampleCube(position.xyz+normal*2-lightPosition)),1)*result : result;
		return result;

	// SPOT
	}else if (lightType==2) {
		float lap=lightAperture;
		float base = (1-pow(clamp(dot(l,lightDirection),0,1),2));
		float lim = pow(map(lightAperture,0,90,0,1),1.8)/2;
		float inCone = (base<lim) ? map(base,lim,lim-pow(lightAperture/90,2)*lightFalloff,0,1) :0;
		float depth=1;
		if (lightShadowed){
			vec4 shadowSpace = bias * lightMatrix * position;
			shadowSpace.xy/=shadowSpace.w;
			if (lightSoftness==0){
			  depth = 1-Texel(lightShadow, vec3(shadowSpace.xy, lightDistance*0.99/lightRange));
		  }else{
		  	// depth = (Texel(lightShadow2, shadowSpace.xy).r<lightDistance*0.99/lightRange)?0:1;
		  	eyeDistance = distance(position.xyz,eyePosition);
		  	depth=	PCSS_DirectionalLight(vec3(shadowSpace.xy,lightDistance) , lightShadow2, (lightSoftness*32)/lightRange);
		  	// return vec4(vec3(depth),1);
		  }
		}
		vec4 result = depth*inCone * vec4(lightColor *lightPower *att(lightDistance,lightRange) 
      * cosTheta 
      ,1);
		return result;

	// DIRECTIONAL
	}else if (lightType==3) {
		float dist = (signedDistance(position.xyz,lightPosition,-lightDirection));
		float dead = (lightDistance<0) ? 0 :1;
		vec3 col = vec3( lightColor *lightPower * att(-dist,lightRange)* cosPhi );
		float depth=1;
		if (lightShadowed){
			vec4 shadowSpace = bias * lightMatrix * position;
		  depth = 1-Texel(lightShadow, vec3(shadowSpace.xy, dist*0.99/lightRange));
		}
		return vec4(col*depth*dead,1);
	}
}

#endif

