extends RefCounted
class_name WowLiquidMaterial

static var _shader: Shader
static var _cache: Dictionary = {}


static func clear_cache() -> void:
	_cache.clear()


static func _ensure_wow_shader_globals() -> void:
	pass


static func build(liquid_id: int) -> ShaderMaterial:
	var key := "adt:%d" % liquid_id
	if _cache.has(key):
		return _cache[key]

	var style := _style_for_liquid(liquid_id)
	var mat := _create_material(style)
	_cache[key] = mat
	return mat


static func build_wmo(material_id: int) -> ShaderMaterial:
	var key := "wmo:%d" % material_id
	if _cache.has(key):
		return _cache[key]

	var style := {
		"color": Color(0.10, 0.30, 0.48, 0.60),
		"foam": Color(0.58, 0.80, 1.00, 0.22),
		"wave_strength": 0.045,
		"wave_scale": 0.095,
		"wave_speed": 0.38,
		"fresnel_power": 2.2,
		"emission_strength": 0.025,
		"magma_mode": 0.0,
	}
	var mat := _create_material(style)
	_cache[key] = mat
	return mat


static func _create_material(style: Dictionary) -> ShaderMaterial:
	var mat := ShaderMaterial.new()
	mat.shader = _get_shader()
	mat.render_priority = 1
	mat.set_shader_parameter("liquid_color", style["color"])
	mat.set_shader_parameter("foam_color", style["foam"])
	mat.set_shader_parameter("wave_strength", float(style["wave_strength"]))
	mat.set_shader_parameter("wave_scale", float(style["wave_scale"]))
	mat.set_shader_parameter("wave_speed", float(style["wave_speed"]))
	mat.set_shader_parameter("alpha_base", (style["color"] as Color).a)
	mat.set_shader_parameter("fresnel_power", float(style["fresnel_power"]))
	mat.set_shader_parameter("emission_strength", float(style["emission_strength"]))
	mat.set_shader_parameter("magma_mode", float(style["magma_mode"]))
	return mat


static func _style_for_liquid(liquid_id: int) -> Dictionary:
	match liquid_id:
		3, 15, 19:
			return {
				"color": Color(0.78, 0.22, 0.03, 0.78),
				"foam": Color(1.00, 0.66, 0.10, 0.38),
				"wave_strength": 0.025,
				"wave_scale": 0.12,
				"wave_speed": 0.22,
				"fresnel_power": 1.7,
				"emission_strength": 0.42,
				"magma_mode": 1.0,
			}
		4, 20:
			return {
				"color": Color(0.16, 0.38, 0.12, 0.66),
				"foam": Color(0.50, 0.80, 0.32, 0.24),
				"wave_strength": 0.035,
				"wave_scale": 0.11,
				"wave_speed": 0.28,
				"fresnel_power": 2.0,
				"emission_strength": 0.05,
				"magma_mode": 0.0,
			}
		2, 14:
			return {
				"color": Color(0.05, 0.16, 0.30, 0.64),
				"foam": Color(0.48, 0.72, 1.00, 0.20),
				"wave_strength": 0.060,
				"wave_scale": 0.075,
				"wave_speed": 0.32,
				"fresnel_power": 2.5,
				"emission_strength": 0.025,
				"magma_mode": 0.0,
			}
		_:
			return {
				"color": Color(0.08, 0.28, 0.46, 0.58),
				"foam": Color(0.58, 0.82, 1.00, 0.24),
				"wave_strength": 0.050,
				"wave_scale": 0.085,
				"wave_speed": 0.36,
				"fresnel_power": 2.3,
				"emission_strength": 0.025,
				"magma_mode": 0.0,
			}


static func _get_shader() -> Shader:
	if _shader:
		return _shader

	_ensure_wow_shader_globals()
	_shader = Shader.new()
	_shader.code = """
shader_type spatial;
render_mode blend_mix, cull_disabled, unshaded;

uniform vec4 liquid_color : source_color = vec4(0.08, 0.28, 0.46, 0.58);
uniform vec4 foam_color : source_color = vec4(0.58, 0.82, 1.00, 0.24);
uniform float wave_strength = 0.05;
uniform float wave_scale = 0.085;
uniform float wave_speed = 0.36;
uniform float alpha_base = 0.58;
uniform float fresnel_power = 2.3;
uniform float emission_strength = 0.025;
uniform float magma_mode = 0.0;

global uniform vec4 wow_fog_color;
global uniform vec2 wow_fog_range;
global uniform float wow_fog_density;

varying vec3 world_pos;
varying vec3 view_pos;

float hash21(vec2 p) {
	return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
}

float noise21(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);
}

vec3 apply_wow_fog(vec3 color) {
	float dist = length(view_pos);
	float range_fog = smoothstep(wow_fog_range.x, wow_fog_range.y, dist);
	float fog_amount = range_fog * clamp(wow_fog_density, 0.0, 1.0);
	return mix(color, wow_fog_color.rgb, fog_amount);
}

void vertex() {
	world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
	view_pos = (MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
	float t = TIME * wave_speed;
	float wave_a = sin(world_pos.x * wave_scale + t);
	float wave_b = cos(world_pos.z * wave_scale * 1.37 - t * 1.21);
	VERTEX.y += (wave_a + wave_b) * wave_strength;
}

void fragment() {
	vec3 n = normalize(NORMAL);
	vec3 v = normalize(VIEW);
	float facing = clamp(abs(dot(n, v)), 0.0, 1.0);
	float fresnel = pow(1.0 - facing, fresnel_power);

	vec2 p = world_pos.xz * wave_scale;
	float n1 = noise21(p * 0.70 + vec2(TIME * wave_speed, -TIME * wave_speed * 0.60));
	float n2 = noise21(p * 1.80 + vec2(-TIME * wave_speed * 0.35, TIME * wave_speed * 0.45));
	float ripple = smoothstep(0.52, 0.86, n1 * 0.65 + n2 * 0.35);

	vec3 color = mix(liquid_color.rgb, foam_color.rgb, ripple * foam_color.a);
	color += fresnel * foam_color.rgb * 0.35;

	if (magma_mode > 0.5) {
		float glow = smoothstep(0.45, 1.0, ripple + fresnel * 0.45);
		color += vec3(1.0, 0.35, 0.05) * glow * 0.45;
		EMISSION = color * max(emission_strength, 0.35);
	} else {
		EMISSION = color * emission_strength;
	}

	ALBEDO = apply_wow_fog(color);
	ALPHA = clamp(alpha_base + fresnel * 0.18 + ripple * 0.06, 0.0, 1.0);
}
"""
	return _shader
