@tool
class_name TerrainControlLayers
extends Resource

## Resource containing artist-controlled terrain layers for procedural generation.
## Includes biome maps, height profiles, masks, and slope limits.

signal layers_changed

@export var resolution: Vector2i = Vector2i(512, 512):
	set(value):
		resolution = value
		emit_changed()
		layers_changed.emit()

@export_group("Biome Control")
@export var biome_map: Texture2D:
	set(value):
		biome_map = value
		emit_changed()
		layers_changed.emit()

@export var height_profile: Curve:
	set(value):
		if height_profile and height_profile.changed.is_connected(_on_curve_changed):
			height_profile.changed.disconnect(_on_curve_changed)
		height_profile = value
		if height_profile:
			height_profile.changed.connect(_on_curve_changed)
		emit_changed()
		layers_changed.emit()

@export_group("Slope Control")
@export var slope_limit_map: Texture2D:
	set(value):
		slope_limit_map = value
		emit_changed()
		layers_changed.emit()

@export_range(0.0, 90.0) var default_slope_limit: float = 45.0:
	set(value):
		default_slope_limit = value
		emit_changed()
		layers_changed.emit()

@export_group("Mask Layers")
@export var mask_layers: Array[Texture2D] = []:
	set(value):
		mask_layers = value
		emit_changed()
		layers_changed.emit()

@export var mask_names: Array[String] = []:
	set(value):
		mask_names = value
		emit_changed()
		layers_changed.emit()


func _on_curve_changed() -> void:
	emit_changed()
	layers_changed.emit()


func get_biome_value(uv: Vector2) -> Color:
	if not biome_map:
		return Color.WHITE
	var img := biome_map.get_image()
	if not img:
		return Color.WHITE
	var px := clampi(int(uv.x * img.get_width()), 0, img.get_width() - 1)
	var py := clampi(int(uv.y * img.get_height()), 0, img.get_height() - 1)
	return img.get_pixel(px, py)


func get_slope_limit(uv: Vector2) -> float:
	if not slope_limit_map:
		return default_slope_limit
	var img := slope_limit_map.get_image()
	if not img:
		return default_slope_limit
	var px := clampi(int(uv.x * img.get_width()), 0, img.get_width() - 1)
	var py := clampi(int(uv.y * img.get_height()), 0, img.get_height() - 1)
	return img.get_pixel(px, py).r * 90.0


func get_height_profile_value(normalized_height: float) -> float:
	if not height_profile:
		return normalized_height
	return height_profile.sample(clampf(normalized_height, 0.0, 1.0))


func get_mask_value(mask_index: int, uv: Vector2) -> float:
	if mask_index < 0 or mask_index >= mask_layers.size():
		return 0.0
	var tex := mask_layers[mask_index]
	if not tex:
		return 0.0
	var img := tex.get_image()
	if not img:
		return 0.0
	var px := clampi(int(uv.x * img.get_width()), 0, img.get_width() - 1)
	var py := clampi(int(uv.y * img.get_height()), 0, img.get_height() - 1)
	return img.get_pixel(px, py).r


func get_mask_name(mask_index: int) -> String:
	if mask_index < 0 or mask_index >= mask_names.size():
		return "mask_%d" % mask_index
	return mask_names[mask_index]
