extends Control
# DropCatcher.gd  (manual-drag version)
# No longer uses Godot's built-in _drop_data. Instead it exposes
# paint_at_screen_pos(), which the trapezoid calls on release.
# This Control no longer needs to catch mouse events at all, so it can be
# tiny/invisible — but keep it in the tree so it can reach the camera & board.

@export var camera: Camera3D
@export var main: Node            # the ROOT node (DartboardProto)

const RAY_LENGTH := 1000.0
var dartboard: MeshInstance3D

# Standard dartboard number sequence, clockwise starting from the TOP (12 o'clock).
const DART_NUMBERS := [20, 1, 18, 4, 13, 6, 10, 15, 2, 17,
					   3, 19, 7, 16, 8, 11, 14, 9, 12, 5]

# Calibration: rotate which sector counts as "top". If the wrong number gets
# hit, bump this by 1 until it lines up. Range 0-19.
@export var sector_offset := 0
# If your board's numbers go counter-clockwise in 3D, flip this.
@export var reverse_direction := false

# --- Radial band boundaries (fractions of outer radius, 0=center 1=rim) ---
# Tune these in the Inspector by reading the norm= prints for each ring.
@export var bull_inner_max := 0.04   # <= this = inner bull (50)
@export var bull_outer_max := 0.09   # <= this = outer bull (25)
@export var inner_max := 0.45        # <= this = inner single
@export var triple_max := 0.55       # <= this = triple ring
@export var outer_max := 0.80        # <= this = outer single; above = double

# Stores which modifier is applied to each surface index: { surface_index: Modifier }
var applied_modifiers: Dictionary = {}

func _ready() -> void:
	# Don't block mouse input; the trapezoids handle their own dragging.
	mouse_filter = Control.MOUSE_FILTER_IGNORE
	print("DropCatcher ready. camera=", camera, " main=", main)

func _get_board() -> MeshInstance3D:
	if dartboard == null and main != null and "dartboard" in main:
		dartboard = main.dartboard
	return dartboard

# Public: try to apply a modifier (paint its texture + store its rule) at a
# screen position. Returns true if it hit the board, false if it missed.
func apply_modifier_at_screen_pos(screen_pos: Vector2, modifier: Resource) -> bool:
	var board := _get_board()
	if board == null:
		push_error("DropCatcher: no dartboard mesh (is 'main' set?).")
		return false
	if camera == null:
		push_error("DropCatcher: camera export not set.")
		return false
	if modifier == null:
		return false

	var ray_origin := camera.project_ray_origin(screen_pos)
	var ray_dir := camera.project_ray_normal(screen_pos)
	var ray_end := ray_origin + ray_dir * RAY_LENGTH

	var space_state := get_viewport().world_3d.direct_space_state
	var query := PhysicsRayQueryParameters3D.create(ray_origin, ray_end)
	query.collide_with_areas = false
	query.collide_with_bodies = true
	var result := space_state.intersect_ray(query)
	if result.is_empty():
		print("  drop missed the board")
		return false

	var surface_index := _surface_from_hit(result, board)
	if surface_index < 0:
		return false

	_paint_surface(board, surface_index, modifier.texture)
	applied_modifiers[surface_index] = modifier      # remember the scoring rule
	print("  applied '", modifier.id, "' to surface ", surface_index)
	return true

# Call this when a dart lands on a surface to get the modified score.
func score_for_surface(surface_index: int, base_value: int) -> int:
	if applied_modifiers.has(surface_index):
		return applied_modifiers[surface_index].apply_score(base_value)
	return base_value

func _surface_from_hit(result: Dictionary, board: MeshInstance3D) -> int:
	var hit_point: Vector3 = result.get("position", Vector3.ZERO)
	var local := board.to_local(hit_point)
	var aabb := board.get_aabb()
	var center := aabb.position + aabb.size * 0.5
	# Board disc lies in the X/Y plane (thin on Z). Measure radius/angle in X/Y.
	var dx := local.x - center.x
	var dy := local.y - center.y
	var radius := Vector2(dx, dy).length()
	var outer_radius : float = max(aabb.size.x, aabb.size.y) * 0.5
	var norm := radius / outer_radius if outer_radius > 0.0 else 0.0
	print("  local=", local, " dx=", "%.3f" % dx, " dy=", "%.3f" % dy, " norm=", "%.3f" % norm)

	# --- RADIAL BAND ---
	# These thresholds are fractions of the outer radius. TUNE to match your art:
	# the real proportions of a dartboard, from center outward.
	var seg_name := ""
	if norm <= bull_inner_max:
		seg_name = "bull_inner"          # inner bull (50)
	elif norm <= bull_outer_max:
		seg_name = "bull_outer"          # outer bull (25)
	elif norm > 1.0:
		print("  outside board")
		return -1
	else:
		# --- ANGULAR SECTOR ---
		# atan2 gives angle of the hit point. We map it to one of 20 sectors,
		# then index into the dartboard number sequence.
		var angle := atan2(dx, dy)        # -PI..PI, 0 = straight up (+y = top)
		# Convert to 0..1 going clockwise from top. Adjust the rotation so 0 = top.
		var t := angle / (2.0 * PI)
		t = fposmod(t, 1.0)
		if reverse_direction:
			t = 1.0 - t
		var sector_index := int(floor(t * 20.0 + 0.5)) % 20
		sector_index = (sector_index + sector_offset) % 20
		var number : int = DART_NUMBERS[sector_index]

		# Which ring band within the sector.
		var band := ""
		if norm <= inner_max:
			band = "inner"               # inner single
		elif norm <= triple_max:
			band = "triple"              # triple ring
		elif norm <= outer_max:
			band = "outer"               # outer single
		else:
			band = "double"             # double ring
		seg_name = "seg_%d_%s" % [number, band]

	print("  norm=", "%.3f" % norm, " -> ", seg_name)
	return _index_of_surface_named(board, seg_name)

func _index_of_surface_named(board: MeshInstance3D, target_name: String) -> int:
	var mesh := board.mesh
	for i in mesh.get_surface_count():
		var mat := mesh.surface_get_material(i)
		if mat and mat.resource_name == target_name:
			return i
	return -1

func _paint_surface(board: MeshInstance3D, surface_index: int, tex: Texture2D) -> void:
	# Always build a fresh StandardMaterial3D so the texture shows as an IMAGE,
	# not tinted by the old base color. If the segment looks like a flat color
	# instead of the pattern, the mesh is missing proper UVs on that face.
	var new_mat := StandardMaterial3D.new()
	new_mat.albedo_color = Color.WHITE          # white so texture isn't tinted
	new_mat.albedo_texture = tex
	new_mat.texture_filter = BaseMaterial3D.TEXTURE_FILTER_LINEAR
	new_mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
	board.set_surface_override_material(surface_index, new_mat)
