#ifndef FOXY_MATERIAL_REFLECTION_POLICY_GLSL
#define FOXY_MATERIAL_REFLECTION_POLICY_GLSL

#include "/lib/surface_pbr.glsl"

float MaterialReflectionSmoothness(const in float perceptualRoughness) {
	return 1.0 - smoothstep(
		FOXY_MATERIAL_REFLECTION_ROUGHNESS_CUTOFF * 0.72,
		FOXY_MATERIAL_REFLECTION_ROUGHNESS_CUTOFF,
		perceptualRoughness
	);
}

vec3 MaterialReflectionAboveSurface(
	const in vec3 direction,
	const in vec3 geometricNormal
) {
	vec3 surfaceNormal = normalize(geometricNormal);
	float geometricSide = dot(direction, surfaceNormal);
	return normalize(direction - 2.0 * min(geometricSide, 0.0) * surfaceNormal);
}

// This follows the same energy test used by efficient material-reflection
// paths: a dielectric only traces while its F0 and roughness can produce a
// visible lobe. The packed GBuffer has no custom dielectric F0 channel,
// so use the pack's existing fallback F0. Metals retain their wider range.
float MaterialReflectionSurfaceEnabled(
	const in float valid,
	const in float surfaceClass,
	const in float perceptualRoughness,
	const in float metalness
) {
	float smoothness = MaterialReflectionSmoothness(perceptualRoughness);
	float vegetation = step(0.5, surfaceClass) * (1.0 - step(3.5, surfaceClass));
	// The RT GBuffer reserves classes 5-7 for emissive payloads. Keep this
	// local to the policy so the lightweight reflection filter has no GBuffer
	// decode dependency before it declares its own read contract.
	float emissive = step(4.5, surfaceClass);
	float roughness = perceptualRoughness * perceptualRoughness;
	float dielectricF0 = max(FOXY_PBR_FALLBACK_F0, 0.02);
	float dielectricEnergy = dielectricF0 * max(
		1.0 - roughness * FOXY_MATERIAL_REFLECTION_ENERGY_ROUGHNESS_ATTENUATION,
		0.0
	);
	float dielectricEligible = step(FOXY_MATERIAL_REFLECTION_ENERGY_THRESHOLD, dielectricEnergy);
	float reflectiveMaterial = max(
		Saturate(metalness),
		dielectricEligible * step(FOXY_MATERIAL_REFLECTION_MIN_SMOOTHNESS, smoothness)
	);
	return valid * (1.0 - vegetation) * (1.0 - emissive) * step(0.002, smoothness) * reflectiveMaterial;
}

// Only screen-space misses can reach the voxel continuation. Preserve smooth
// metals and grazing reflections, while preventing diffuse, off-screen VRTGI
// work whose lobe is below visible energy.
float MaterialReflectionGlobalEnabled(
	const in float smoothness,
	const in float f0Energy,
	const in float NoV
) {
	float grazingFresnel = pow(1.0 - Saturate(NoV), 5.0);
	float visibleEnergy = smoothness * max(f0Energy, grazingFresnel);
	return step(FOXY_MATERIAL_REFLECTION_GLOBAL_MIN_ENERGY, visibleEnergy);
}

#endif
