#version 450

layout (binding = 1) uniform sampler2D samplerColor;

struct Gui {
	vec4 offset;
	vec4 color;
	int mode;
	float depth;
	int sdf;
	int shadowbox;
	vec4 stroke;
	float weight;
	int spread;
	float scale;
};

layout (location = 0) in vec2 inUv;
layout (location = 1) in flat Gui inGui;
layout (location = 0) out vec4 outFragColor;

const int indexMatrix8x8[64] = int[](0,  32, 8,  40, 2,  34, 10, 42,
									 48, 16, 56, 24, 50, 18, 58, 26,
									 12, 44, 4,  36, 14, 46, 6,  38,
									 60, 28, 52, 20, 62, 30, 54, 22,
									 3,  35, 11, 43, 1,  33, 9,  41,
									 51, 19, 59, 27, 49, 17, 57, 25,
									 15, 47, 7,  39, 13, 45, 5,  37,
									 63, 31, 55, 23, 61, 29, 53, 21);

float indexValue8x8() {
	int x = int(mod(gl_FragCoord.x, 8));
	int y = int(mod(gl_FragCoord.y, 8));
	return indexMatrix8x8[(x + y * 8)] / 64.0;
}

const int paletteSize = 28;
const vec3 palette[28] = vec3[](
	vec3(0, 0, 0),
	vec3(0, 0, 1),
	vec3(0, 0, 0.502),
	vec3(0, 0, 0.753),
	vec3(0, 1, 0.251),
	vec3(0, 1, 0.50),
	vec3(60.0/360.0, 1, 0.251),
	vec3(60.0/360.0, 1, 0.5),
	vec3(120.0/360.0, 1, 0.251),
	vec3(120.0/360.0, 1, 0.5),
	vec3(180.0/360.0, 1, 0.251),
	vec3(180.0/360.0, 1, 0.5),
	vec3(240.0/360.0, 1, 0.251),
	vec3(240.0/360.0, 1, 0.5),
	vec3(300.0/360.0, 1, 0.251),
	vec3(300.0/360.0, 1, 0.5),
	vec3(60.0/360.0, 0.333, 0.376),
	vec3(60.0/360.0, 1, 0.751),
	vec3(180.0/360.0, 1, 0.125),
	vec3(150.0/360.0, 1, 0.5),
	vec3(210.0/360.0, 1, 0.5),
	vec3(180.0/360.0, 1, 0.751),
	vec3(210.0/360.0, 1, 0.251),
	vec3(240.0/360.0, 1, 0.751),
	vec3(270.0/360.0, 1, 0.5),
	vec3(330.0/360.0, 1, 0.5),
	vec3(30.0/360.0, 1, 0.251),
	vec3(24.0/360.0, 1, 0.625)
);

vec3 hslToRgb(vec3 HSL) {
	vec3 RGB; {
		float H = HSL.x;
		float R = abs(H * 6 - 3) - 1;
		float G = 2 - abs(H * 6 - 2);
		float B = 2 - abs(H * 6 - 4);
		RGB = clamp(vec3(R,G,B), 0, 1);
	}
	float C = (1 - abs(2 * HSL.z - 1)) * HSL.y;
	return (RGB - 0.5) * C + HSL.z;
}

vec3 rgbToHsl(vec3 RGB) {
	float Epsilon = 1e-10;
	vec3 HCV; {
		vec4 P = (RGB.g < RGB.b) ? vec4(RGB.bg, -1.0, 2.0/3.0) : vec4(RGB.gb, 0.0, -1.0/3.0);
		vec4 Q = (RGB.r < P.x) ? vec4(P.xyw, RGB.r) : vec4(RGB.r, P.yzx);
		float C = Q.x - min(Q.w, Q.y);
		float H = abs((Q.w - Q.y) / (6 * C + Epsilon) + Q.z);
		HCV = vec3(H, C, Q.x);
	}
	float L = HCV.z - HCV.y * 0.5;
	float S = HCV.y / (1 - abs(L * 2 - 1) + Epsilon);
	return vec3(HCV.x, S, L);
}

float hueDistance(float h1, float h2) {
	float diff = abs((h1 - h2));
	return min(abs((1.0 - diff)), diff);
}

vec3[2] closestColors(float hue) {
	vec3 ret[2];
	vec3 closest = vec3(-2, 0, 0);
	vec3 secondClosest = vec3(-2, 0, 0);
	vec3 temp;
	for (int i = 0; i < paletteSize; ++i) {
		temp = palette[i];
		float tempDistance = hueDistance(temp.x, hue);
		if (tempDistance < hueDistance(closest.x, hue)) {
			secondClosest = closest;
			closest = temp;
		} else {
			if (tempDistance < hueDistance(secondClosest.x, hue)) {
				secondClosest = temp;
			}
		}
	}
	ret[0] = closest;
	ret[1] = secondClosest;
	return ret;
}

const float lightnessSteps = 4.0;
float lightnessStep(float l) {
    /* Quantize the lightness to one of `lightnessSteps` values */
    return floor((0.5 + l * lightnessSteps)) / lightnessSteps;
}

vec3 dither1(vec3 color) {
    vec3 hsl = rgbToHsl(color);

    vec3 cs[2] = closestColors(hsl.x);
    vec3 c1 = cs[0];
    vec3 c2 = cs[1];
    float d = indexValue8x8();
    float hueDiff = hueDistance(hsl.x, c1.x) / hueDistance(c2.x, c1.x);

    float l1 = lightnessStep(max((hsl.z - 0.125), 0.0));
    float l2 = lightnessStep(min((hsl.z + 0.124), 1.0));
    float lightnessDiff = (hsl.z - l1) / (l2 - l1);

    vec3 resultColor = (hueDiff < d) ? c1 : c2;
    //resultColor.z = (lightnessDiff < d) ? l1 : l2;
    return hslToRgb(resultColor);
}

float dither(float color) {
	float closestColor = (color < 0.5) ? 0 : 1;
	float secondClosestColor = 1 - closestColor;
	float d = indexValue8x8();
	float distance = abs(closestColor - color);
	return (distance < d) ? closestColor : secondClosestColor;
}

vec3 dither(vec3 color) { 
	vec3 hsl = rgbToHsl(color);
	hsl.y = dither(hsl.y);
	return hslToRgb(hsl);
}

void main() {
	if ( inUv.x < inGui.offset.x ) discard;
	if ( inUv.y < inGui.offset.y ) discard;
	if ( inUv.x > inGui.offset.z ) discard;
	if ( inUv.y > inGui.offset.w ) discard;

	if ( inGui.shadowbox == 1 ) {
		outFragColor = inGui.color;
		return;
	}
	float dist = texture(samplerColor, inUv).r;
	if ( inGui.sdf == 1 ) {
		float smoothing = ( inGui.spread > 0 && inGui.scale > 0 ) ? 0.25 / (inGui.spread * inGui.scale) : 0.25 / (4 * 1.5);
		float outlining = smoothstep(0.5 - smoothing, 0.5 + smoothing, dist);
		float alpha = smoothstep(inGui.weight - smoothing, inGui.weight + smoothing, dist);
		vec4 c = inGui.color;
		outFragColor = mix(inGui.stroke, c, outlining);
		outFragColor.a = inGui.color.a * alpha;
		if ( alpha < 0.001 ) discard;
		if ( alpha > 1 ) discard;
	} else {
		outFragColor = vec4(inGui.color) * dist;
		if ( dist < 0.001 ) discard;
		if ( dist > 1 ) discard;
	}

	outFragColor.rgb = dither(outFragColor.rgb);
}