#include "luminance.glsllib"

vec3 RGBToYPbPr( in vec3 v )
{
    vec3 ypp;
    ypp.x = luminance( v );
    ypp.y = 0.5 * (v.b - ypp.x) / (1.0 - yCoeff_709.b);
    ypp.z = 0.5 * (v.r - ypp.x) / (1.0 - yCoeff_709.r);

    return ypp;
}

vec3 YPbPrToRGB( in vec3 v )
{
    vec3 outRGB;
    outRGB.x = dot(vec3(1.0, 0.0, 1.575), v);
    outRGB.y = dot(vec3(1.0, -0.187, -0.468), v);
    outRGB.z = dot(vec3(1.0, 1.856, 0.0), v);

    return outRGB;
}

float remapLum( float inLum, float whitePt )
{
        return clamp( inLum / whitePt, 0.0, 1.0 );
}

float exposeLum( float inLum, float exposure )
{
        return 1.0 - exp2( -inLum / exposure );
}

vec3 gammaCorrect( vec3 inColor, float gammaExp )
{
        return pow( inColor, vec3( 1.0 / gammaExp ) );
}

vec3 adjSaturation( vec3 inRGB, float satFactor )
{
        // Must be done in linear space (before gamma correction)
        float P = sqrt( luminance( inRGB * inRGB ) );
        vec3 outCol;
        outCol = (inRGB - vec3(P)) * satFactor;
        outCol += vec3(P);
        return outCol;
}

float curveCompute( float inLum, float slope0, float slope1 )
{
        float a1 = slope0;
        float a2 = 3.0 - 2.0 * slope0 - slope1;
        float a3 = slope1 + slope0 - 2.0;

        // Cubic curve fit.  This results in a curve that is 0 where inColor is 0
        // equals 1 when inColor is 1, and the derivative at 0 is slope0 and the
        // derivative at 1 is slope1
        return ((((a3 * inLum) + a2)*inLum) + a1)*inLum;
}

float toeEmphasize( float inParam )
{
        float a1 = 1.0 - toeEmphasis;
        float a2 = 2.0 * toeEmphasis;
        float a3 = -toeEmphasis;

        return ((((a3 * inParam) + a2) * inParam) + a1) * inParam;
}

float shoulderEmphasize( float inParam )
{
        float a1 = 1.0;
        float a2 = shoulderEmphasis;
        float a3 = -shoulderEmphasis;

        return ((((a3 * inParam) + a2) * inParam) + a1) * inParam;
}

void frag()
{
        // k = shadow slope, m = midtone slope, n = highlight slope
        float k = toeSlope;
        float m = 1.0 + contrastBoost;
        float n = shoulderSlope;

        //vec4 sourceColor = texture(SourceSampler, TexCoord);
        vec4 sourceColor = texture(Texture0, TexCoord);
        vec3 sourceSep = RGBToYPbPr(sourceColor.rgb);

        float lum = sourceSep.r;

        if (useExposure)
            lum = exposeLum( lum, exposureValue );
        else
            lum = remapLum( lum, whitePoint );

        float param0 = toeEmphasize( 2.0 * lum );               // Parametrization for Curve Part 1
        float param1 = shoulderEmphasize( 2.0 * lum - 1.0 );    // Parametrization for Curve Part 2

        float lum0 = 0.5 * curveCompute( param0, k, m );
        float lum1 = 0.5 * curveCompute( param1, m, n ) + 0.5;
        sourceSep.r = (lum > 0.5) ? lum1 : lum0;

        // Convert back to RGB and gamma correct
        vec3 finalColor = YPbPrToRGB( sourceSep );
        finalColor = gammaCorrect( adjSaturation( finalColor, saturationLevel ), gammaValue );
        gl_FragColor = vec4( finalColor, sourceColor.a );
}
