layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
uint BuildLpvMask(const in uint north, const in uint east, const in uint south, const in uint west, const in uint up, const in uint down)
{
    return east | (west << 1) | (down << 2) | (up << 3) | (south << 4) | (north << 5);
}

const ivec3 workGroups = ivec3(6, 6, 1);
// #define LPV_ENABLED
#ifdef LPV_ENABLED
#include "/lib/lpv/items.glsl"
#include "/lib/lpv/blocks.glsl"
#include "/lib/lpv/entities.glsl"
#include "/lib/lpv/lpv_blocks.glsl"

// Color Groups in GLSL

// Group 1: White
// Group 1: White
const vec3 LightColor_White = vec3(0.9, 0.75, 0.6);

// Group 2: Light Blue
const vec3 LightColor_LightBlue = vec3(0.4, 0.6, 0.7);

// Group 3: Cyan
const vec3 LightColor_Cyan = vec3(0.35, 0.7, 0.7);

// Group 4: Blue
const vec3 LightColor_Blue = vec3(0.2, 0.4, 0.7);

// Group 5: Magenta
const vec3 LightColor_Magenta = vec3(0.6, 0.4, 0.6);

// Group 6: Purple
const vec3 LightColor_Purple = vec3(0.5, 0.3, 0.7);

// Group 7: Yellow
const vec3 LightColor_Yellow = vec3(0.8, 0.8, 0.4);

// Group 8: Orange
const vec3 LightColor_Orange = vec3(0.9, 0.6, 0.4);

// Group 9: Red
const vec3 LightColor_Red = vec3(0.6, 0.3, 0.3);

// Group 10: Pink
const vec3 LightColor_Pink = vec3(0.9, 0.6, 0.7);

// Group 11: Brown
const vec3 LightColor_Brown = vec3(0.5, 0.4, 0.3);

// Group 12: Green
const vec3 LightColor_Green = vec3(0.4, 0.6, 0.4);

// Group 13: Lime
const vec3 LightColor_Lime = vec3(0.5, 0.7, 0.3);

// Group 14: Other / Fire
const vec3 LightColor_Black = vec3(0.9, 0.6, 0.4);

//////////////

mat4 GetSaturationMatrix(const in float saturation)
{
    const vec3 luminance = vec3(0.3086, 0.6094, 0.0820);

    vec3 lumSat = luminance * (1.0 - saturation);
    vec2 satZero = vec2(saturation, 0.0);

    return mat4(
        vec4(lumSat.r + satZero.xyy, 0.0),
        vec4(lumSat.g + satZero.yxy, 0.0),
        vec4(lumSat.b + satZero.yyx, 0.0),
        vec4(0.0, 0.0, 0.0, 1.0));
}
#endif

void main()
{
#ifdef LPV_ENABLED
    int blockId = int(gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * 48);
    if (blockId >= 2048)
        return;

    vec3 lightColor = vec3(0.0);
    float lightRange = 0.0;
    float mixWeight = 0.5; // reduces the "ao"
    uint mixMask = 0xFFFF;
    vec3 tintColor = vec3(1.0);

    switch (blockId)
    {
    case BLOCK_WATER:
        tintColor = vec3(0.8, 1.0, 0.95);

        mixWeight = 0.8;

        break;

    case BLOOP_BLOCK_OTHER:
        mixWeight = 0.8;
        break;

    case BLOOP_BLOCK_FLOWERS:
    case BLOOP_BLOCK_FLOWERS_TALL_UPPER:
    case BLOOP_BLOCK_FLOWERS_TALL_LOWER:
    case BLOOP_BLOCK_FLOWERS_MODDED:
        mixWeight = 0.85;
        break;

    case BLOOP_BLOCK_LEAVES:
        mixWeight = 0.9;
        break;
    }

    // lightsources
    if (blockId >= 100 && blockId <= 324)
    {
        mixWeight = 1.0;

        // Determine lightRange
        switch ((blockId - 100) % 16)
        {
        case 0:
            lightRange = 1;
            break;
        case 1:
            lightRange = 2;
            break;
        case 2:
            lightRange = 3;
            break;
        case 3:
            lightRange = 4;
            break;
        case 4:
            lightRange = 5;
            break;
        case 5:
            lightRange = 6;
            break;
        case 6:
            lightRange = 7;
            break;
        case 7:
            lightRange = 8;
            break;
        case 8:
            lightRange = 9;
            break;
        case 9:
            lightRange = 10;
            break;
        case 10:
            lightRange = 11;
            break;
        case 11:
            lightRange = 12;
            break;
        case 12:
            lightRange = 13;
            break;
        case 13:
            lightRange = 14;
            break;
        case 14:
            lightRange = 15;
            break;
        case 15:
            lightRange = 16;
            break;
        }

        // Determine lightColor
        switch ((blockId - 100) / 16)
        {
        case 0:
            lightColor = LightColor_White;
            break;
        case 1:
            lightColor = LightColor_LightBlue;
            break;
        case 2:
            lightColor = LightColor_Cyan;
            break;
        case 3:
            lightColor = LightColor_Blue;
            break;
        case 4:
            lightColor = LightColor_Magenta;
            break;
        case 5:
            lightColor = LightColor_Purple;
            break;
        case 6:
            lightColor = LightColor_Yellow;
            break;
        case 7:
            lightColor = LightColor_Orange;
            break;
        case 8:
            lightColor = LightColor_Red;
            break;
        case 9:
            lightColor = LightColor_Pink;
            break;
        case 10:
            lightColor = LightColor_Brown;
            break;
        case 11:
            lightColor = LightColor_Green;
            break;
        case 12:
            lightColor = LightColor_Lime;
            break;
        case 13:
            lightColor = LightColor_Black;
            break;
        }
    }

    // reflective translucents / glass

    switch (blockId)
    {
    case 50:
        tintColor = vec3(1.0);
        mixWeight = 1.0;
        break;
    case 51:
        tintColor = vec3(0.3);
        mixWeight = 1.0;
        break;
    case 52:
        tintColor = vec3(0.1, 0.1, 0.98);
        mixWeight = 1.0;
        break;
    case 53:
        tintColor = vec3(0.566, 0.388, 0.148);
        mixWeight = 1.0;
        break;
    case 54:
        tintColor = vec3(0.082, 0.533, 0.763);
        mixWeight = 1.0;
        break;
    case 55:
        tintColor = vec3(0.4, 0.4, 0.4);
        mixWeight = 1.0;
        break;
    case 56:
        tintColor = vec3(0.125, 0.808, 0.081);
        mixWeight = 1.0;
        break;
    case 57:
        tintColor = vec3(0.320, 0.685, 0.955);
        mixWeight = 1.0;
        break;
    case 58:
        tintColor = vec3(0.7);
        mixWeight = 1.0;
        break;
    case 59:
        tintColor = vec3(0.633, 0.924, 0.124);
        mixWeight = 1.0;
        break;
    case 60:
        tintColor = vec3(0.698, 0.298, 0.847);
        mixWeight = 1.0;
        break;
    case 61:
        tintColor = vec3(0.919, 0.586, 0.185);
        mixWeight = 1.0;
        break;
    case 62:
        tintColor = vec3(0.949, 0.274, 0.497);
        mixWeight = 1.0;
        break;
    case 63:
        tintColor = vec3(0.578, 0.170, 0.904);
        mixWeight = 1.0;
        break;
    case 64:
        tintColor = vec3(0.999, 0.188, 0.188);
        mixWeight = 1.0;
        break;
    case 65:
        tintColor = vec3(0.96, 0.96, 0.96);
        mixWeight = 1.0;
        break;
    case 66:
        tintColor = vec3(0.965, 0.965, 0.123);
        mixWeight = 1.0;
        break;
    case 67:
        tintColor = vec3(0.984, 0.733, 0.251);
        mixWeight = 1.0;
        break;
    case 68:
        tintColor = vec3(0.502, 0.165, 0.831);
        mixWeight = 1.0;
        lightColor = LightColor_Magenta;
        lightRange = 11;
        break;
    case 69:
        tintColor = vec3(0.408, 0.725, 0.329);
        mixWeight = 1.0;
        break;
    }
    // LPV shapes
    switch (blockId)
    {
    case BLOCK_LPV_IGNORE:
        mixWeight = 1.0;
        break;

    case BLOCK_CARPET:
        mixMask = BuildLpvMask(1u, 1u, 1u, 1u, 1u, 0u);
        mixWeight = 0.9;
        break;

    case BLOCK_DOOR_N:
        mixMask = BuildLpvMask(0u, 1u, 1u, 1u, 1u, 1u);
        mixWeight = 0.8;
        break;
    case BLOCK_DOOR_E:
        mixMask = BuildLpvMask(1u, 0u, 1u, 1u, 1u, 1u);
        mixWeight = 0.8;
        break;
    case BLOCK_DOOR_S:
        mixMask = BuildLpvMask(1u, 1u, 0u, 1u, 1u, 1u);
        mixWeight = 0.8;
        break;
    case BLOCK_DOOR_W:
        mixMask = BuildLpvMask(1u, 1u, 1u, 0u, 1u, 1u);
        mixWeight = 0.8;
        break;

    case BLOCK_FENCE:
    case BLOCK_FENCE_GATE:
        mixWeight = 0.7;
        break;
    case BLOCK_FLOWER_POT:
        mixWeight = 0.7;
        break;
    case BLOCK_IRON_BARS:
        mixWeight = 0.6;
        break;
    case BLOCK_PRESSURE_PLATE:
        mixMask = BuildLpvMask(1u, 1u, 1u, 1u, 1u, 0u);
        mixWeight = 0.9;
        break;

    case BLOCK_SLAB_TOP:
        mixMask = BuildLpvMask(1u, 1u, 1u, 1u, 0u, 1u);
        mixWeight = 0.5;
        break;
    case BLOCK_SLAB_BOTTOM:
    case BLOCK_SNOW_LAYERS:
        mixMask = BuildLpvMask(1u, 1u, 1u, 1u, 1u, 0u);
        mixWeight = 0.5;
        break;

    case BLOCK_TRAPDOOR_BOTTOM:
        mixMask = BuildLpvMask(1u, 1u, 1u, 1u, 1u, 0u);
        mixWeight = 0.8;
        break;
    case BLOCK_TRAPDOOR_TOP:
        mixMask = BuildLpvMask(1u, 1u, 1u, 1u, 0u, 1u);
        mixWeight = 0.8;
        break;
    case BLOCK_TRAPDOOR_N:
        mixMask = BuildLpvMask(0u, 1u, 1u, 1u, 1u, 1u);
        mixWeight = 0.8;
        break;
    case BLOCK_TRAPDOOR_E:
        mixMask = BuildLpvMask(1u, 0u, 1u, 1u, 1u, 1u);
        mixWeight = 0.8;
        break;
    case BLOCK_TRAPDOOR_S:
        mixMask = BuildLpvMask(1u, 1u, 0u, 1u, 1u, 1u);
        mixWeight = 0.8;
        break;
    case BLOCK_TRAPDOOR_W:
        mixMask = BuildLpvMask(1u, 1u, 1u, 0u, 1u, 1u);
        mixWeight = 0.8;
        break;
    }

    // WALL
    if (blockId >= BLOCK_WALL_MIN && blockId <= BLOCK_WALL_MAX)
    {
        mixWeight = 0.25;

        if (blockId == BLOCK_WALL_POST_TALL_ALL || blockId == BLOCK_WALL_TALL_ALL || blockId == BLOCK_WALL_POST_TALL_N_W_S || blockId == BLOCK_WALL_POST_TALL_N_E_S || blockId == BLOCK_WALL_POST_TALL_W_N_E || blockId == BLOCK_WALL_POST_TALL_W_S_E)
        {
            mixMask = BuildLpvMask(0u, 0u, 0u, 0u, 1u, 1u);
            mixWeight = 0.125;
        }
        else if (blockId == BLOCK_WALL_POST_TALL_N_S || blockId == BLOCK_WALL_TALL_N_S)
        {
            mixMask = BuildLpvMask(1u, 0u, 1u, 0u, 1u, 1u);
        }
        else if (blockId == BLOCK_WALL_POST_TALL_W_E || blockId == BLOCK_WALL_TALL_W_E)
        {
            mixMask = BuildLpvMask(0u, 1u, 0u, 1u, 1u, 1u);
        }
        // TODO: more walls
    }

    // Misc

    if (blockId == BLOCK_SIGN)
    {
        mixWeight = 0.9;
    }

    // Entities

    if (blockId == ENTITY_BLAZE)
    {
        lightColor = vec3(1.000, 0.592, 0.000);
        lightRange = 8.0;
    }

    if (blockId == ENTITY_END_CRYSTAL)
    {
        lightColor = vec3(1.000, 0.000, 1.000);
        lightRange = 8.0;
    }

    if (blockId == ENTITY_FIREBALL_SMALL)
    {
        lightColor = vec3(0.000, 1.000, 0.000);
        lightRange = 8.0;
        mixWeight = 1.0;
    }

    if (blockId == ENTITY_GLOW_SQUID)
    {
        lightColor = vec3(0.180, 0.675, 0.761);
        lightRange = 6.0;
        mixWeight = 0.5;
    }

    if (blockId == ENTITY_MAGMA_CUBE)
    {
        lightColor = vec3(0.747, 0.323, 0.110);
        lightRange = 9.0;
    }

    if (blockId == ENTITY_TNT)
    {
        lightColor = vec3(1.0);
        lightRange = 8.0;
    }

    if (blockId == ENTITY_SPECTRAL_ARROW)
    {
        lightColor = vec3(0.839, 0.541, 0.2);
        lightRange = 8.0;
        mixWeight = 1.0;
    }



    // apply saturation changes to light color
    const float saturationF = LPV_SATURATION / 100.0;
    mat4 matSaturation = GetSaturationMatrix(saturationF);
    lightColor = (matSaturation * vec4(lightColor, 1.0)).rgb;

    // apply saturation changes to tint color
    const float tintSaturationF = LPV_TINT_SATURATION / 100.0;
    mat4 matTintSaturation = GetSaturationMatrix(tintSaturationF);
    tintColor = (matTintSaturation * vec4(tintColor, 1.0)).rgb;

    // lazy fix for migrating from mixWeight to tintColor
    tintColor *= mixWeight;

    uint lightColorRange = packUnorm4x8(vec4(lightColor, lightRange / 255.0));
    uint tintColorMask = packUnorm4x8(vec4(tintColor, 0.0));
    tintColorMask |= mixMask << 24;

    imageStore(imgBlockData, blockId, uvec4(lightColorRange, tintColorMask, 0u, 0u));
#endif
}
