#include "/Shaders/Internal/light_shared.swsl"

highp vec4 calcGaussianWeights(highp float sigma, highp vec4 offset)
{
    highp vec4 eExp = offset * offset / (2.0 * sigma * sigma);
    return exp(-eExp) / (sigma * sqrt(2.0 * PI));
}

highp float createOcclusion(highp vec2 diff)
{
    // Calculate vector perpendicular to light vector.
    // So we can sample it to get a decent soft shadow?
    highp vec2 perpendicular = normalize(cross(vec3(diff, 0.0), vec3(0.0, 0.0, 1.0)).xy) * 1.0 / 32.0;

    highp float ourDist = length(diff);

    // Sample 7 points on a line perpendicular to the light source.
    // Depending on the closest point, we change the gaussian weights down below
    // to change the "narrowness" of the samples.
    perpendicular *= lightSoftness * 1.5;

    // Get all the samples we need.
    highp vec2 sample1 = occludeDepth(diff, shadowMap, lightIndex);
    highp vec2 sample2 = occludeDepth(diff + perpendicular, shadowMap, lightIndex);
    highp vec2 sample3 = occludeDepth(diff - perpendicular, shadowMap, lightIndex);
    highp vec2 sample4 = occludeDepth(diff + perpendicular * 2.0, shadowMap, lightIndex);
    highp vec2 sample5 = occludeDepth(diff - perpendicular * 2.0, shadowMap, lightIndex);
    highp vec2 sample6 = occludeDepth(diff + perpendicular * 3.0, shadowMap, lightIndex);
    highp vec2 sample7 = occludeDepth(diff - perpendicular * 3.0, shadowMap, lightIndex);

    highp float mindist =
        min(sample1.x,
        min(sample2.x,
        min(sample3.x,
        min(sample4.x,
        min(sample5.x,
        min(sample6.x,
            sample7.x))))));

    mindist = max(0.001, mindist);

    // Change soft shadow size based on distance from primary occluder.
    highp float distRatio = (ourDist - mindist);

    // Sigma can never be zero so make sure to clamp.
    // TODO: Scaling the dist ratio here in a more sane way might make shadows look better buuuut I'm lazy.
    // Shadows look pretty nice already.
    highp float sigma = max(0.001, distRatio * 0.75);
    highp vec4 weights = calcGaussianWeights(sigma, vec4(0.0, 1.0, 2.0, 3.0));

    // Calculation of gaussian weights here is broken because it doesn't add up to 1.
    // Fixing this is hard and if I had to guess too expensive for GPU shaders.
    // So instead we add up the total weights and scale the result with that,
    // so that we still end up with 0-1.
    highp float totalWeigths = weights.x + weights.y * 2.0 + weights.z * 2.0 + weights.w * 2.0;

    highp float occlusion = 0.0;

    // Calculate actual occlusion with new weights.
    occlusion += ChebyshevUpperBound(sample1, ourDist) * weights.x;
    occlusion += ChebyshevUpperBound(sample2, ourDist) * weights.y;
    occlusion += ChebyshevUpperBound(sample3, ourDist) * weights.y;
    occlusion += ChebyshevUpperBound(sample4, ourDist) * weights.z;
    occlusion += ChebyshevUpperBound(sample5, ourDist) * weights.z;
    occlusion += ChebyshevUpperBound(sample6, ourDist) * weights.w;
    occlusion += ChebyshevUpperBound(sample7, ourDist) * weights.w;

    return occlusion / totalWeigths;
}

