#version 460

#define M_PI 3.14159265359

layout(location = 0) in vec2 inUV;

layout(location = 0) out vec4 outColor;
// layout(location = 1) out vec4 outBloomThreshold;

layout(set = 1, binding = 1) uniform sampler2D inAlbedo;
layout(set = 1, binding = 2) uniform sampler2D inNormal;
layout(set = 1, binding = 3) uniform sampler2D inPosition;
layout(set = 1, binding = 4) uniform sampler2D inPbr;
// layout(set = 1, binding = 5) uniform sampler2D shadowMap;

float ggx_normal_distr(float NoH, float roughness) {
	float a = NoH * roughness;
	float k = roughness / (1.0 - NoH * NoH + a * a);
	return k * k * (1.0 / M_PI);
}

vec3 schlick(float VoH, vec3 f0) {
	return f0 + (vec3(1.0) - f0) * pow(1.0 - VoH, 5.0);
}

float ggx_smith_correlated(float NoV, float NoL, float a) {
	float a2 = a * a;
	float GGXL = NoV * sqrt((-NoL * a2 + NoL) * NoL + a2);
	float GGXV = NoL * sqrt((-NoV * a2 + NoV) * NoV + a2);
	return 0.5 / (GGXV + GGXL + 0.0001);
}


vec3 lambert_diffuse(vec3 albedo) {
	return albedo / M_PI;
}

layout(set = 0, binding = 0) uniform Camera {
	mat4 proj;
	mat4 view;
	vec4 position;
} cam;

struct Light {
	mat4 view;
	vec4 color;
	vec4 position;
};

layout(set = 1, binding = 0) uniform Lights {
	Light lights[];
} lights;

#define AMBIENT 0.1

float linearize_depth(float depth) {
	float n = 1000.0f;
	float f = 1.0f;
	float z = depth;
	return (2.0 * n) / (f + n - z * (f - n));
}

float textureProj(vec4 shadowCoord, vec2 off) {
	return 0.0f;
	/* float shadow = 0.0f;

	float dist = texture( shadowMap, shadowCoord.st + off ).r;
	if ( shadowCoord.w > 0.0 )
	{
		shadow = (dist < shadowCoord.z) ? 1.0f : 0.0f;
	}
	return shadow; */
}

float filterPCF(vec4 sc) {
	return 0.0f;
	/* ivec2 texDim = textureSize(shadowMap, 0);
	float scale = 6.0f;
	float dx = scale * 1.0 / float(texDim.x);
	float dy = scale * 1.0 / float(texDim.y);

	float shadowFactor = 0.0;
	int count = 0;
	int range = 12;

	for (int x = -range; x <= range; x++)
	{
		for (int y = -range; y <= range; y++)
		{
			shadowFactor += textureProj(sc, vec2(dx*x, dy*y));
			count++;
		}

	}
	return shadowFactor / count; */
}

const mat4 biasMat = mat4(
	0.5, 0.0, 0.0, 0.0,
	0.0, 0.5, 0.0, 0.0,
	0.0, 0.0, 1.0, 0.0,
	0.5, 0.5, 0.0, 1.0
);


float get_shadow(int lightIdx, vec4 position) {
	Light light = lights.lights[0];
	vec4 relativePosition = light.view * position;
	vec4 shadowCoords = relativePosition / relativePosition.w;

	float shadow = filterPCF(biasMat * shadowCoords);

	return shadow;
}

vec3 bsdf(vec3 normal, vec3 viewDir, vec3 lightDir, vec3 baseColor, float metallic, float roughness) {
	vec3 halfway = normalize(viewDir + lightDir);
	float NoH = max(0.0, dot(normal, halfway));
	float NoL = max(0.0, dot(normal, lightDir));
	float NoV = abs(dot(normal, viewDir)) + 1e-5;
	float VoH = max(0.0, dot(viewDir, halfway));
	float LoH = max(0.0, dot(lightDir, halfway));


	/* color at 0 degree angle */
	vec3 f0 = 0.16 * (1.0 - roughness) * (1.0 - roughness) * (1.0 - metallic) + baseColor * metallic;
	vec3 diffuseColor = (1.0 - metallic) * baseColor.rgb;

	/* normal distribution of the dome */
	float D = ggx_normal_distr(NoH, roughness);
	/* occlusion of microfacets */
	float G = ggx_smith_correlated(NoV, NoL, roughness);
	/* fresnel */
	vec3  F = schlick(VoH, f0);

	vec3 specular = D * G * F; /// (4.0 * NoV * NoL + 0.0001);
	/* light emmited back by material (not reflection) */

	vec3 diffuse = lambert_diffuse(diffuseColor);

	return (diffuse + specular) * NoL * 4.0f;
}

vec3 bloom_threshold(vec3 base) {
	float threshold = 0.8f;
	float knee = 0.5f;
	float intensity = 0.4f;
	vec4 curveThreshold = vec4(threshold - knee, knee * 2.0f, 0.25f / max(1e-5f, knee), threshold);

	// Pixel brightness
	float br = max((base).x, max((base).y, (base).z));

	// Under-threshold part: quadratic curve
	float rq = clamp(br - curveThreshold.x, 0.0, curveThreshold.y);
	rq = curveThreshold.z * rq * rq;

	// Combine and apply the brightness response curve.
	base *= max(rq, br - curveThreshold.w) / max(1e-5, br);

	// Clamp pixel intensity if clamping enabled
	if (intensity > 0.0) {
		br = max(1e-5, max((base).x, max((base).y, (base).z)));
		base *= 1.0 - max(0.0, br - intensity) / br;
	}

	return base;
}

void main() {
	vec3 color = texture(inAlbedo, inUV).rgb;
	float metallic = texture(inPbr, inUV).r;
	float roughness = texture(inPbr, inUV).g;
	roughness *= roughness;
	vec3 normal = texture(inNormal, inUV).rgb;
	vec3 position = texture(inPosition, inUV).rgb;

	vec3 viewDir = normalize(-cam.position.xyz - position);

	vec3 lightDir = -normalize(vec3(lights.lights[0].view[0][2], lights.lights[0].view[1][2], lights.lights[0].view[2][2]));

	float occlusion = get_shadow(0, vec4(position, 1.0));

	float lightIntensity = 1.5f;
	vec3 Lo = bsdf(normal, viewDir, lightDir, color, metallic, roughness);

	vec3 ambient = vec3(0.2) * color;
	vec3 prd = ambient + Lo * (1.0 - occlusion) * lightIntensity;

	outColor = vec4(prd, 1.0);

	// outBloomThreshold = vec4(bloom_threshold(prd), dot(bloom_threshold(prd), bloom_threshold(prd)));
}
