// #type vertex
#version 460 core

layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 aUV;

layout (location = 0) out vec2 oUV;

void main()
{
    oUV = aUV;
    gl_Position = vec4(aPos, 0.0, 1.0);
}

// #type fragment
#version 460 core

precision highp float;

// ConsoleColor vec3 palette (normalized)
const vec3 ConsoleColorVec3[16] = vec3[](
    vec3(0.0, 0.0, 0.0), // Black
    vec3(0.0, 0.0, 0.5), // DarkBlue
    vec3(0.0, 0.5, 0.0), // DarkGreen
    vec3(0.0, 0.5, 0.5), // DarkCyan
    vec3(0.5, 0.0, 0.0), // DarkRed
    vec3(0.5, 0.0, 0.5), // DarkMagenta
    vec3(0.5, 0.5, 0.0), // DarkYellow
    vec3(0.5, 0.5, 0.5), // Gray
    vec3(0.25, 0.25, 0.25), // DarkGray
    vec3(0.0, 0.0, 1.0), // Blue
    vec3(0.0, 1.0, 0.0), // Green
    vec3(0.0, 1.0, 1.0), // Cyan
    vec3(1.0, 0.0, 0.0), // Red
    vec3(1.0, 0.0, 1.0), // Magenta
    vec3(1.0, 1.0, 0.0), // Yellow
    vec3(1.0, 1.0, 1.0)  // White
);

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

vec3 rgbToXyz(vec3 color) {
    const mat3 rgbToXyzMatrix = mat3(
        0.4124564, 0.3575761, 0.1804375,
        0.2126729, 0.7151522, 0.0721750,
        0.0193339, 0.1191920, 0.9503041
    );
    return rgbToXyzMatrix * color;
}

vec3 xyzToLab(vec3 xyz) {
    const vec3 whitePoint = vec3(0.95047, 1.0, 1.08883); // D65 white point
    xyz = xyz / whitePoint;

    vec3 f = mix(pow(xyz, vec3(1.0 / 3.0)), 7.787 * xyz + 16.0 / 116.0, step(0.008856, xyz));
    return vec3(
        (116.0 * f.y) - 16.0,
        500.0 * (f.x - f.y),
        200.0 * (f.y - f.z)
    );
}

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);
}

float perceptualColorDistance(vec3 color1, vec3 color2) {
    vec3 lab1 = xyzToLab(rgbToXyz(srgbToLinear(color1)));
    vec3 lab2 = xyzToLab(rgbToXyz(srgbToLinear(color2)));
    vec3 delta = lab1 - lab2;

    return dot(delta, delta);
}

int findMostPerceptuallyAccurateColor(vec3 color) {
    int bestMatchIndex = 0;
    float minDistance = perceptualColorDistance(color, ConsoleColorVec3[0]);

    for (int i = 1; i < 16; i++) {
        float currentDistance = perceptualColorDistance(color, ConsoleColorVec3[i]);
        if (currentDistance < minDistance) {
            minDistance = currentDistance;
            bestMatchIndex = i;
        }
    }

    return bestMatchIndex;
}

//int findMostPerceptuallyAccurateColor(vec3 color) {
//    int closestIndex = 0;
//    float minDistance = distance(color, ConsoleColorVec3[0]);
//
//    for (int i = 1; i < 16; i++) {
//        float dist = distance(color, ConsoleColorVec3[i]);
//        if (dist < minDistance) {
//            minDistance = dist;
//            closestIndex = i;
//        }
//    }
//
//    return closestIndex;
//}

// Enhanced luminosity calculation considering human perception
float calculatePerceptualLuminance(vec3 color) {
    // BT.709 luminance coefficients with slight adjustment
    return pow(
        0.2126 * pow(color.r, 2.2) +
        0.7152 * pow(color.g, 2.2) +
        0.0722 * pow(color.b, 2.2),
        1.0 / 2.2
    );
}

// Dithering function to reduce color banding
float interleavedGradientNoise(vec2 pixel) {
    return fract(52.982919 * fract(0.06711056 * pixel.x + 0.00583715 * pixel.y));
}

uniform sampler2D uInputTexture;

layout (location = 0) in vec2 iUV;
layout (location = 0) out vec4 FragColor;

void main() {
    vec3 pixelColor = texture(uInputTexture, iUV * 4).rgb;

    // Find most perceptually accurate console color
    int colorIndex = findMostPerceptuallyAccurateColor(pixelColor);

    // Calculate perceptual luminance with gamma correction
    float luminance = calculatePerceptualLuminance(pixelColor);

    // Output with high precision color mapping
    FragColor = vec4(
        luminance, // Red: Perceptual luminance
        float(colorIndex) / 15.0, // Green: Normalized color index
        0.0, // Blue: Unused
        1.0 // Alpha
    );
}