#ifndef COMMON_GLSL
#define COMMON_GLSL

// Include the shared constants.
#include "../src/Vk/ShaderConst.hpp"

#define LIGHT_SHININESS                     256.0

vec4 alphaBlend(vec4 backgroundColor, vec4 foregroundColor)
{
    return vec4(
        foregroundColor.rgb * foregroundColor.a + backgroundColor.rgb * (1.0 - foregroundColor.a), 
        foregroundColor.a + (1.0 - foregroundColor.a) * backgroundColor.a
    );
}

vec3 refract(vec3 I, vec3 N, float n1, float n2, out bool reflected)
{
    float eta = n1 / n2;
    float cosi = dot(N, I);
    float k = 1.0 - eta * eta * (1.0 - cosi * cosi);

    if (k < 0.0)
    {
        reflected = true;
        return reflect(I, N);
    }
    else
    {
        reflected = false;
        return eta * I - (eta * cosi + sqrt(k)) * N;
    }
}

uint hash(uint x)
{
    x += (x << 10u);
    x ^= (x >>  6u);
    x += (x <<  3u);
    x ^= (x >> 11u);
    x += (x << 15u);
    return x;
}

vec3 sRGBToLinear(vec3 srgb)
{
    return mix(srgb / 12.92, pow((srgb + 0.055) / 1.055, vec3(2.4)), step(vec3(0.04045), srgb));
}

vec3 rgb2hsv(vec3 c)
{
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));

    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

vec3 hsv2rgb(vec3 c)
{
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

float noiseFromHash(uint m)
{
    const uint ieeeMantissa = 0x007FFFFFu; // binary32 mantissa bitmask
    const uint ieeeOne      = 0x3F800000u; // 1.0 in IEEE binary32

    m &= ieeeMantissa;                     // Keep only mantissa bits (fractional part)
    m |= ieeeOne;                          // Add fractional part to 1.0

    float  f = uintBitsToFloat(m);         // Range [1:2]
    float partial =  f - 1.0;              // Range [0:1]
    return partial * 2.0 - 1.0;            // Range [-1:1]
}

vec3 noiseFromHash3(uint p)
{
    return vec3(
        noiseFromHash(hash(p ^ 0x9ee52a1c)),
        noiseFromHash(hash(p ^ 0x10aab71d)),
        noiseFromHash(hash(p ^ 0xb99a2626))
    );
}

vec3 calculateNoise(ivec3 pos)
{
    uint hx = hash(pos.x ^ 0x525af2c0);
    uint hy = hash(pos.y ^ 0xbd2b0122);
    uint hz = hash(pos.z ^ 0xa71f6637);
    uint p = hx ^ hy ^ hz;

    return noiseFromHash3(p);
}

float unitFromHash(uint m)
{
    const uint ieeeMantissa = 0x007FFFFFu;
    const uint ieeeOne      = 0x3F800000u;

    m &= ieeeMantissa;
    m |= ieeeOne;

    float  f = uintBitsToFloat(m);
    return f - 1.0;              // Range [0:1]
}

vec3 unitNoiseFromNumber(uint p)
{
    return vec3(
        unitFromHash(hash(p ^ 0x9ee52a1c)),
        unitFromHash(hash(p ^ 0x10aab71d)),
        unitFromHash(hash(p ^ 0xb99a2626))
    );
}

vec4 decodeSRGBA8(uint rgba8)
{
    vec4 result = vec4(
        (rgba8 >> 24) & 0xFF,
        (rgba8 >> 16) & 0xFF,
        (rgba8 >> 8) & 0xFF,
        rgba8 & 0xFF
    ) / 255.0;

    return vec4(sRGBToLinear(result.rgb), result.a);
}

vec4 decodeXYZW8(uint xyzw8)
{
    return vec4(
        (xyzw8 >> 24) & 0xFF,
        (xyzw8 >> 16) & 0xFF,
        (xyzw8 >> 8) & 0xFF,
        xyzw8 & 0xFF
    ) / 255.0;
}

#endif