# Attach this script to the STENCIL TextureRect (UI)

extends TextureRect

@export var rotation_speed: float = 2.0

# RayCast3D from the camera center (still needed for pickups)
@export var interact_raycast: RayCast3D

# Assign the player node here
@export var player_node: Node3D

@export var match_label: Label

var just_pressed := false

# ----------------------------
# Stored Stencils
# ----------------------------
var collected_textures: Array[Texture2D] = []
var collected_texture_paths: Array[String] = []

var current_index: int = -1


# Save file path
const SAVE_PATH := "res://stencil_save.json"

# How close values must be to count as a match
const POSITION_TOLERANCE := 3.0
const ROTATION_TOLERANCE := 0.40

# ----------------------------
# Highlight Pickups
# ----------------------------
var highlighted_object: MeshInstance3D = null

func _ready() -> void:
	# Start hidden until something is collected
	visible = false

	# Optional: start with no texture
	texture = null
	
	if match_label != null:
		match_label.visible = false


func _process(delta: float) -> void:
	_handle_rotation(delta)
	_handle_pickup()
	_handle_highlight()


func _input(event) -> void:
	just_pressed = event.is_pressed() and not event.is_echo()

	_handle_stencil_switch()

	# Ctrl+P save shortcut
	if event is InputEventKey:
		if event.ctrl_pressed and event.keycode == KEY_P and just_pressed:
			_save_all_stencil_states()

	# Press P to compare current state
	if event is InputEventKey:
		if event.keycode == KEY_P and just_pressed and not event.ctrl_pressed:
			_check_stencil_match()


# ----------------------------
# Rotation (Q / E)
# ----------------------------
func _handle_rotation(delta: float) -> void:
	var rotate_dir := 0.0

	if not Input.is_key_pressed(KEY_CTRL) and Input.is_key_pressed(KEY_Q):
		rotate_dir += 1.0
	if Input.is_key_pressed(KEY_E):
		rotate_dir -= 1.0

	if rotate_dir != 0.0:
		rotation += rotate_dir * rotation_speed * delta


# ----------------------------
# Pickup Collection (F)
# ----------------------------
func _handle_pickup() -> void:
	if interact_raycast == null:
		return

	if not Input.is_key_pressed(KEY_F):
		return

	if not interact_raycast.is_colliding():
		return

	var hit_object = interact_raycast.get_collider()

	if not hit_object.is_in_group("pickup"):
		return

	var pickup_parent = hit_object.get_parent()

	if pickup_parent != null:
		if pickup_parent is MeshInstance3D:
			var pickup_mesh := pickup_parent as MeshInstance3D
			var pickup_mat = pickup_mesh.get_active_material(0)

			if pickup_mat is StandardMaterial3D:
				var tex: Texture2D = pickup_mat.albedo_texture
				if tex != null:
					_add_stencil_texture(tex, tex.resource_path)

		pickup_parent.queue_free()
	else:
		print("Missing pickup MeshInstance3D as parent")


# ----------------------------
# Store New Texture
# ----------------------------
func _add_stencil_texture(tex: Texture2D, tex_path: String) -> void:
	collected_textures.append(tex)
	collected_texture_paths.append(tex_path)

	current_index = collected_textures.size() - 1
	_apply_current_texture()

	visible = true


# ----------------------------
# Switching Stencils (Z / C)
# ----------------------------
func _handle_stencil_switch() -> void:
	if collected_textures.is_empty():
		return

	if Input.is_key_pressed(KEY_Z) and just_pressed:
		_cycle_stencil(-1)

	if Input.is_key_pressed(KEY_C) and just_pressed:
		_cycle_stencil(1)


func _cycle_stencil(direction: int) -> void:
	current_index += direction

	if current_index < 0:
		current_index = collected_textures.size() - 1
	elif current_index >= collected_textures.size():
		current_index = 0

	_apply_current_texture()


# ----------------------------
# Apply Current Texture
# ----------------------------
func _apply_current_texture() -> void:
	if current_index < 0 or current_index >= collected_textures.size():
		return

	texture = collected_textures[current_index]


# ============================================================
# SAVE SYSTEM (Ctrl + P)
# ============================================================

func _save_all_stencil_states() -> void:
	if player_node == null:
		print("ERROR: No player_node assigned.")
		return

	if current_index < 0:
		print("No active stencil selected.")
		return

	# Load existing save file
	var save_data: Array = []
	if FileAccess.file_exists(SAVE_PATH):
		var file := FileAccess.open(SAVE_PATH, FileAccess.READ)
		if file:
			var content = file.get_as_text()
			file.close()
			if content.strip_edges() != "":
				save_data = JSON.parse_string(content)

	# Entry for current stencil
	var entry := {
		"texture_path": collected_texture_paths[current_index],
		"player_position": {
			"x": player_node.global_position.x,
			"y": player_node.global_position.y,
			"z": player_node.global_position.z
		},
		"player_rotation": {
			"x": player_node.global_rotation.x,
			"y": player_node.global_rotation.y,
			"z": player_node.global_rotation.z
		},
		"stencil_rotation": rotation
	}

	# Update or append
	var updated := false
	for i in range(save_data.size()):
		if save_data[i]["texture_path"] == entry["texture_path"]:
			save_data[i] = entry
			updated = true
			break

	if not updated:
		save_data.append(entry)

	_write_json_file(save_data)

	print("Saved stencil:", entry["texture_path"])


func _write_json_file(data: Array) -> void:
	var file := FileAccess.open(SAVE_PATH, FileAccess.WRITE)
	if file == null:
		print("ERROR: Could not open save file.")
		return

	file.store_string(JSON.stringify(data, "\t"))
	file.close()


# ============================================================
# MATCH CHECK (P)
# ============================================================

func _check_stencil_match() -> void:
	if current_index < 0:
		print("No active stencil.")
		return

	if not FileAccess.file_exists(SAVE_PATH):
		print("No save file found.")
		return

	var file := FileAccess.open(SAVE_PATH, FileAccess.READ)
	var parsed = JSON.parse_string(file.get_as_text())
	file.close()

	var active_path := collected_texture_paths[current_index]

	for entry in parsed:
		if entry["texture_path"] != active_path:
			continue

		var saved_pos := Vector3(
			entry["player_position"]["x"],
			entry["player_position"]["y"],
			entry["player_position"]["z"]
		)

		var saved_player_rot := Vector3(
			entry["player_rotation"]["x"],
			entry["player_rotation"]["y"],
			entry["player_rotation"]["z"]
		)

		var current_pos := player_node.global_position
		var current_player_rot := player_node.global_rotation
		var pos_close := current_pos.distance_to(saved_pos) <= POSITION_TOLERANCE

		var saved_rot = entry["stencil_rotation"]
		var player_rot_close := current_player_rot.distance_to(saved_player_rot) <= ROTATION_TOLERANCE
		var rot_close = abs(rotation - saved_rot) <= ROTATION_TOLERANCE

		if pos_close and player_rot_close and rot_close:
			match_found()
		else:
			print("❌ No match.")

		return

	print("No saved entry found.")


func match_found() -> void:
	print("✅ MATCH!")

	# ============================================================
	# DOOR CHECK (Raycast forward)
	# ============================================================
	if interact_raycast != null and interact_raycast.is_colliding():
		var hit_object = interact_raycast.get_collider()

		# Only trigger doors
		if hit_object != null and hit_object.is_in_group("door"):
			print("🚪 Door detected!")

			# Try to find an AnimationPlayer on the door object
			var anim_player: AnimationPlayer = hit_object.get_node_or_null("AnimationPlayer")

			if anim_player != null:
				print("▶ Playing door animation...")
				anim_player.play("play")
			else:
				print("⚠ Door has no AnimationPlayer node.")

	# ============================================================
	# REMOVE THE MATCHED STENCIL
	# ============================================================
	if current_index >= 0 and current_index < collected_textures.size():
		collected_textures.remove_at(current_index)
		collected_texture_paths.remove_at(current_index)

	# If no stencils remain, hide everything
	if collected_textures.is_empty():
		current_index = -1
		texture = null
		visible = false
	else:
		# Clamp index and show next available stencil
		if current_index >= collected_textures.size():
			current_index = collected_textures.size() - 1

		_apply_current_texture()

	# ============================================================
	# SHOW MATCH LABEL FOR 5 SECONDS
	# ============================================================
	if match_label != null:
		match_label.text = "MATCH"
		match_label.visible = true

		await get_tree().create_timer(5.0).timeout

		match_label.visible = false

# HIGHLIGHTS 

func _handle_highlight() -> void:
	if interact_raycast == null:
		return

	var collider = interact_raycast.get_collider()

	# Remove highlight from previous object
	if highlighted_object != null and highlighted_object != collider:
		_set_emission(highlighted_object, false)
		highlighted_object = null

	# Highlight currently looked-at pickup
	if collider != null and collider.is_in_group("pickup"):
		var parent_mesh = collider.get_parent() as MeshInstance3D
		if parent_mesh != null and highlighted_object != parent_mesh:
			highlighted_object = parent_mesh
			_set_emission(parent_mesh, true)
	elif highlighted_object != null:
		_set_emission(highlighted_object, false)
		highlighted_object = null

func _set_emission(mesh: MeshInstance3D, enable: bool) -> void:
	var mat = mesh.material_override
	if mat is StandardMaterial3D:
		mat.emission_enabled = enable
		mat.emission = Color(1, 1, 0) if enable else Color(0, 0, 0)
