// Shared helpers for the environment shaders (env_*.gdshader).
// Ground layout (road / plaza / ponds) must match scripts/env_layout.gd (uniforms set per map).

// sRGB (0-1) -> linear, so palette values below read like the style guide hex colors.
vec3 srgb(vec3 c) {
	return pow(c, vec3(2.2));
}

float hash21(vec2 p) {
	p = fract(p * vec2(123.34, 456.21));
	p += dot(p, p + 45.32);
	return fract(p.x * p.y);
}

vec2 hash22(vec2 p) {
	float n = hash21(p);
	return vec2(n, hash21(p + n + 17.17));
}

float vnoise(vec2 p) {
	vec2 i = floor(p);
	vec2 f = fract(p);
	vec2 u = f * f * (3.0 - 2.0 * f);
	float a = hash21(i);
	float b = hash21(i + vec2(1.0, 0.0));
	float c = hash21(i + vec2(0.0, 1.0));
	float d = hash21(i + vec2(1.0, 1.0));
	return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}

float fbm(vec2 p) {
	float v = 0.0;
	float a = 0.5;
	for (int i = 0; i < 4; i++) {
		v += a * vnoise(p);
		p = p * 2.03 + vec2(11.7, 5.3);
		a *= 0.5;
	}
	return v / 0.9375;
}

// ---- per-map palette (sRGB, set from data/maps.json "look" by env_layout.gd; defaults = Aldoria)
uniform int ground_kind = 0;                            // 0 lawn, 1 desert sand, 2 cave floor, 3 snow
uniform vec3 lawn_dark = vec3(0.33, 0.52, 0.17);        // #54852b
uniform vec3 lawn_mid = vec3(0.40, 0.59, 0.21);         // #649634
uniform vec3 lawn_light = vec3(0.50, 0.66, 0.24);       // #7fa83d
uniform vec3 lawn_warm = vec3(0.53, 0.60, 0.25);        // olive #879840
uniform float lawn_warm_amt = 0.5;
uniform vec3 sand_light = vec3(0.98, 0.93, 0.79);       // #fbedc9
uniform vec3 sand_mid = vec3(0.94, 0.85, 0.63);         // #f0d8a0
uniform vec3 sand_shade = vec3(0.85, 0.70, 0.48);       // #d8b27a
uniform vec3 hardpan = vec3(0.85, 0.66, 0.47);          // #d8a878
uniform vec3 dry_grass = vec3(0.78, 0.69, 0.38);        // #c8b060
uniform vec3 oasis_grass = vec3(0.50, 0.70, 0.26);
uniform vec3 floor_a = vec3(0.36, 0.31, 0.34);         // cave floor: packed earth / stone
uniform vec3 floor_b = vec3(0.45, 0.39, 0.38);
uniform vec3 floor_moss = vec3(0.27, 0.36, 0.25);

// Painted lawn color (docs/STYLE_GUIDE.md: lawn #649434): large soft color patches, little noise.
vec3 lawn_color(vec2 p) {
	float n1 = fbm(p * 0.03 + vec2(3.1, 7.7));
	float n2 = vnoise(p * 0.15 + vec2(40.0, 9.0));
	vec3 dark = srgb(lawn_dark);
	vec3 mid = srgb(lawn_mid);
	vec3 light = srgb(lawn_light);
	vec3 col = mix(dark, mid, smoothstep(0.30, 0.52, n1));
	col = mix(col, light, smoothstep(0.56, 0.80, n1));
	// warm olive patches (#879840) / leaf litter in the forest
	float warm = smoothstep(0.60, 0.85, fbm(p * 0.018 + vec2(-20.0, 4.0)));
	col = mix(col, srgb(lawn_warm), warm * lawn_warm_amt);
	col *= 0.95 + 0.08 * n2;
	return col;
}

// Desert sand: long wind-shaped dune bands (lit crest, mauve-ish lee), big soft patches, fine ripples.
vec3 sand_color(vec2 p) {
	float n1 = fbm(p * 0.022 + vec2(3.1, 7.7));
	float warp = fbm(p * 0.035 + vec2(8.0, -3.0)) * 6.0;
	float ridge = sin(p.x * 0.10 + p.y * 0.045 + warp);
	vec3 col = mix(srgb(sand_mid), srgb(sand_light), smoothstep(0.35, 0.72, n1));
	col = mix(col, srgb(sand_shade), (1.0 - smoothstep(-0.95, -0.25, ridge)) * 0.6);
	col = mix(col, srgb(sand_light), smoothstep(0.55, 0.95, ridge) * 0.35);
	float rip = sin(dot(p, vec2(1.7, 0.6)) + vnoise(p * 0.5) * 3.0);
	col *= 0.975 + 0.035 * rip;
	return col;
}

// Cave floor: big soft stone/earth patches, damp darker streaks, a few mossy spots.
vec3 cave_color(vec2 p) {
	float n1 = fbm(p * 0.05 + vec2(1.3, 8.1));
	float n2 = vnoise(p * 0.4 + vec2(5.0, 2.0));
	vec3 col = mix(srgb(floor_a), srgb(floor_b), smoothstep(0.3, 0.75, n1));
	float damp = smoothstep(0.62, 0.8, fbm(p * 0.03 + vec2(-7.0, 3.0)));
	col = mix(col, col * 0.72, damp);
	float moss = smoothstep(0.7, 0.85, fbm(p * 0.07 + vec2(12.0, -4.0)));
	col = mix(col, srgb(floor_moss), moss * 0.6);
	col *= 0.94 + 0.1 * n2;
	return col;
}

// Snow (ground_kind 3, Frostveil Tundra): soft white drifts, blue-lilac hollows, a faint wind ripple.
vec3 snow_color(vec2 p) {
	float n1 = fbm(p * 0.025 + vec2(6.1, 2.7));
	float warp = fbm(p * 0.04 + vec2(-3.0, 9.0)) * 5.0;
	float drift = sin(p.x * 0.07 - p.y * 0.05 + warp);
	vec3 col = mix(srgb(vec3(0.86, 0.90, 0.97)), srgb(vec3(0.97, 0.98, 1.0)), smoothstep(0.3, 0.72, n1));
	col = mix(col, srgb(vec3(0.76, 0.80, 0.93)), (1.0 - smoothstep(-0.9, -0.2, drift)) * 0.45);
	col *= 0.985 + 0.02 * sin(dot(p, vec2(1.3, 0.9)) + vnoise(p * 0.6) * 3.0);
	return col;
}

// Base ground color of the active map (grass shaders sample it under every tuft too).
vec3 grass_color(vec2 p) {
	if (ground_kind == 1) {
		return sand_color(p);
	}
	if (ground_kind == 3) {
		return snow_color(p);
	}
	if (ground_kind == 2) {
		return cave_color(p);
	}
	return lawn_color(p);
}

// ---- ground layout (set per map by env_layout.gd::set_shader; defaults = Aldoria Meadows) ----
uniform vec2 town_c = vec2(-48.0, 0.0);
uniform float town_half = 17.0;
uniform float town_on = 1.0;
uniform vec3 pond_a = vec3(-40.0, -46.0, 7.0);   // x, z, radius (radius 0 = no pond)
uniform vec3 pond_b = vec3(44.0, 47.0, 6.0);
uniform vec3 pond_c = vec3(0.0);
uniform vec3 pond_d = vec3(0.0);
uniform float road_on = 1.0;
uniform vec4 road_wave = vec4(0.6, 0.09, 0.5, 0.0);   // amp, freq, phase, z
uniform vec3 road_span = vec3(-34.0, 62.0, 0.0);      // x0, x1, slope (z per x)

float road_center(float x) {
	return road_wave.w + road_span.z * x + road_wave.x * sin(x * road_wave.y + road_wave.z);
}

float road_half(float x) {
	return 1.75 + 0.25 * sin(x * 0.53) + 0.15 * sin(x * 1.37 + 1.0);
}

// 1 on the dirt road, 0 on grass (soft edge).
float road_mask(vec2 p, float edge_noise) {
	if (road_on < 0.5) {
		return 0.0;
	}
	float d = abs(p.y - road_center(p.x)) - road_half(p.x) + edge_noise;
	float m = 1.0 - smoothstep(-0.3, 0.3, d);
	float ends = smoothstep(road_span.x - 2.0, road_span.x + 4.0, p.x) * (1.0 - smoothstep(road_span.y - 4.0, road_span.y + 2.0, p.x));
	return m * ends;
}

// Signed distance to the rounded-square plaza (negative inside).
float plaza_sdf(vec2 p) {
	vec2 q = abs(p - town_c) - vec2(town_half - 5.0);
	float d = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - 5.0;
	float a = atan(p.y - town_c.y, p.x - town_c.x);
	return d + 0.45 * sin(a * 9.0 + 1.0) + 0.3 * sin(a * 23.0);
}

float pond_r(vec3 pond, vec2 p) {
	float a = atan(p.y - pond.y, p.x - pond.x);
	return pond.z * (1.0 + 0.12 * sin(3.0 * a + 1.0) + 0.07 * sin(5.0 * a + 2.0));
}

float pond_one(vec3 pond, vec2 p) {
	return pond.z > 0.0 ? length(p - pond.xy) - pond_r(pond, p) : 1e4;
}

// Signed distance-ish to the nearest pond shore (negative = in water).
float pond_sdf(vec2 p) {
	return min(min(pond_one(pond_a, p), pond_one(pond_b, p)), min(pond_one(pond_c, p), pond_one(pond_d, p)));
}

// ---- wind (shared by env_prop trees/bushes and the CC0 foliage in env_foliage) ----------------
// World-space sway offset for a vertex `h` metres above its plant's origin; amp = metres per metre of
// height. Slow swing + gust envelope, phase-shifted per plant so a forest does not move in lockstep.
vec3 env_wind(vec3 origin, float h, float amp) {
	float t = TIME * 1.4 + origin.x * 0.23 + origin.z * 0.19;
	float gust = 0.6 + 0.4 * sin(TIME * 0.5 + origin.x * 0.05);
	return vec3(sin(t), 0.0, cos(t * 0.83 + 1.1) * 0.6) * amp * gust * h;
}

// ---- cel lighting (same model as toon.gdshader, tuned for scenery) --------
// Style guide: shadows are colored, never grey. Warm ground/foliage -> mauve, white stone -> blue-teal.
const vec3 SHADE_MAUVE = vec3(0.50, 0.40, 0.56);
const vec3 SHADE_TEAL = vec3(0.36, 0.52, 0.61);

// Local (omni / spot) lights: plain additive falloff, NO shade floor. The cel band below adds a
// constant "shadow side" color even where ATTENUATION is 0; for the sun that is intended, but a
// local light's light() only runs inside the screen clusters its sphere touches, so a constant
// term shows up as bright blocky tiles around every small light (fire wall glow).
vec3 env_local_light(vec3 n, vec3 l, float atten, vec3 light_color) {
	float wrap = smoothstep(-0.25, 0.6, dot(n, l));
	return wrap * atten * light_color;
}

// shade_color = shadow/lit ratio (the lavender ambient adds a little on top).
vec3 env_toon(vec3 n, vec3 l, vec3 v, float atten, float soft, float shade_amount, vec3 shade_color, float rim_amount) {
	float ndl = dot(n, l);
	float lit = smoothstep(-soft, soft, ndl);
	lit *= smoothstep(0.3, 0.7, atten);
	vec3 shade = shade_color * shade_amount;
	vec3 band = mix(shade, vec3(1.0), lit);
	float rim = pow(1.0 - clamp(dot(n, v), 0.0, 1.0), 4.0);
	return band + vec3(rim * rim_amount * (0.35 + 0.65 * lit));
}
