#ifndef BLOCK_TINT_GLSL_
#define BLOCK_TINT_GLSL_

vec3 applyTint(vec3 color, vec4 tint)
{
    if (tint.a < 1e-5)
    {
        return color;
    }

    float lum = dot(color, vec3(0.299, 0.587, 0.114));
    tint *= mix(1.0, 3.0, smoothstep(0.20, 0.85, lum));
    float brightnessFactor = mix(0.88, 0.10, lum);
    return clamp(color * brightnessFactor + tint.rgb, 0.0, 1.0);
}

#ifndef BLOCK_TINT_DESC_SET
// Returns the tint color for a block at the given position.
// The A channel is the amount of blending requested.
// This is the dummy implementation; always returns no tint.
vec4 getTint(uvec3 blockPos)
{
    return vec4(0.0);
}
#else

struct HashTableNode
{
    uint entries[GLSL_TINT_TABLE_NODE_SIZE];
};

struct TintEntry
{
    uvec4 blockPosAndTint;
};

layout(
    std430,
    set = BLOCK_TINT_DESC_SET,
    binding = GLSL_TINT_HASH_TABLE_BINDING_INDEX
) readonly buffer BlockTintHashTableBuffer
{
    HashTableNode tintHashTable[];
};

layout(
    std430,
    set = BLOCK_TINT_DESC_SET,
    binding = GLSL_TINT_TABLE_BINDING_INDEX
) readonly buffer TintEntryBuffer
{
    TintEntry tintEntries[];
};

uint posHash(uvec3 blockPos, uint depth)
{
    uint a = hash(uint(blockPos.x) * 73856093u + depth);
    uint b = hash(uint(blockPos.y) * 19349663u + depth);
    uint c = hash(uint(blockPos.z) * 83492791u + depth);

    return a ^ b ^ c;
}

vec4 getTint(uvec3 blockPos)
{
    uint currentNodeIndex = 0;
    vec4 result = vec4(0.0);

    for (uint depth = 0;; depth++)
    {
        uint hash = posHash(blockPos, depth);
        uint entry = tintHashTable[currentNodeIndex].entries[hash % GLSL_TINT_TABLE_NODE_SIZE];

        if ((entry & GLSL_TINT_TABLE_NODE_RESOLVED) != 0)
        {
            uvec4 blockPosAndTint = tintEntries[entry & ~GLSL_TINT_TABLE_NODE_RESOLVED].blockPosAndTint;
            if (blockPosAndTint.xyz != blockPos)
            {
                break;
            }

            result = unpackUnorm4x8(blockPosAndTint.w);
            break;
        }

        if (entry == 0)
        {
            break;
        }

        currentNodeIndex = entry;
    }

    return result;
}

#endif // BLOCK_TINT_DESC_SET

#endif