in lowp vec2 tc;
out vec4 fragColor;

uniform vec2 dims;
uniform float u_scale;
uniform float u_size;
uniform float u_colour;
uniform float u_offset;
uniform float u_offset2;
uniform float u_offset3;
uniform float u_rings;

const float PI      = 3.14159265;
const float SQRT3   = 1.7320508;
const float SQRT3_2 = 0.8660254;

vec2 axialToWorld(vec2 h, float s) {
	return vec2(s * (h.x + h.y * 0.5),
				s * h.y * SQRT3_2);
}

vec2 worldToAxial(vec2 p, float s) {
	return vec2((p.x - p.y / SQRT3) / s,
				(2.0 * p.y / SQRT3) / s);
}

vec2 hexRound(vec2 f) {
	float s  = -f.x - f.y;
	float rq = floor(f.x + 0.5);
	float rr = floor(f.y + 0.5);
	float rs = floor(s   + 0.5);
	float dq = abs(rq - f.x);
	float dr = abs(rr - f.y);
	float ds = abs(rs - s);
	if      (dq > dr && dq > ds) rq = -rr - rs;
	else if (dr > ds)            rr = -rq - rs;
	return vec2(rq, rr);
}

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

void main() {
	// tc is 0-1, convert to centered aspect-correct coords like the WebGL version
	vec2 uv = (tc - 0.5) * dims / min(dims.x, dims.y);

	// Rotation (13 degrees)
	const float ROT = 13.0 * PI / 180.0;
	float ca = cos(ROT);
	float sa = sin(ROT);
	uv = mat2(ca, sa, -sa, ca) * uv;

	// Domain warp 1: flowing low-frequency distortion
	vec2 p = uv / u_scale;
	vec2 warp1 = vec2(
		sin(p.y * 2.1 + 1.3) + 0.6 * sin((p.x + p.y) * 3.7 + 0.7),
		cos(p.x * 2.1 + 2.7) + 0.6 * cos((p.x - p.y) * 3.7 + 1.1)
	);
	uv += warp1 * u_offset * u_scale * 0.35;

	// Domain warp 2: tighter, more turbulent pattern
	p = uv / u_scale;
	vec2 warp2 = vec2(
		cos(p.x * 4.3 - 0.9) + 0.5 * sin((p.y - p.x) * 1.7 + 2.1),
		sin(p.y * 4.3 + 1.7) + 0.5 * cos((p.x + p.y) * 1.7 - 0.3)
	);
	uv += warp2 * u_offset2 * u_scale * 0.35;

	// Domain warp 3: cyclic — 0 and 1 both zero, peaks at 0.5
	p = uv / u_scale;
	float cyc = sin(u_offset3 * PI);
	vec2 warp3 = vec2(
		sin(p.y * 5.3 + p.x * 0.7 + 3.1) + 0.7 * cos((p.x - p.y) * 2.3 - 1.5),
		cos(p.x * 5.3 - p.y * 0.7 + 0.4) + 0.7 * sin((p.x + p.y) * 2.3 + 2.9)
	);
	uv += warp3 * cyc * u_scale * 0.4;

	// Size: 0 = tiny dots, 1 = fills screen
	float radius = u_scale * (0.1 + u_size * 0.7);

	vec2 cell = hexRound(worldToAxial(uv, u_scale));

	// 3x3 neighbourhood
	float field = 0.0;
	float r2 = radius * radius;

	for (int i = -1; i <= 1; i++) {
		for (int j = -1; j <= 1; j++) {
			vec2 hex = cell + vec2(float(i), float(j));
			vec2 centre = axialToWorld(hex, u_scale);
			vec2 d = uv - centre;
			float d2 = dot(d, d);
			float w = max(0.0, 1.0 - d2 / r2);
			field += w;
		}
	}

	// Sharp threshold
	float thresh = step(0.5, field);

	// Sharp concentric contour rings
	float ringPattern = step(0.0, cos(field * u_rings * PI * 2.0));
	float blend = smoothstep(0.0, 2.0, u_rings);
	float v = mix(thresh, ringPattern, blend);

	// Colour: 0 = mono (white on black), 1 = inverted mono (black on white)
	float t   = u_colour;
	float sat = 0.5 + 0.5 * sin(t * PI);
	float hue = t;

	float vi = mix(1.0 - t, max(1.0 - t, 0.8), sat);
	float vo = mix(t,       min(t,       0.2), sat);

	vec3 colOutside  = hsv2rgb(vec3(hue,       sat, vi));
	vec3 colInside = hsv2rgb(vec3(hue + 0.5, sat, vo));

	fragColor = vec4(mix(colOutside, colInside, v), 1.0);
}
