# Attach this script to the STENCIL MeshInstance3D

extends MeshInstance3D

@export var rotation_speed: float = 2.0

# RayCast3D from the camera center
@export var interact_raycast: RayCast3D

# Assign the player node here (CharacterBody3D, Node3D, etc.)
@export var player_node: Node3D

# Material used by the stencil
@export var base_material: StandardMaterial3D

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 := 1.0   # meters
const ROTATION_TOLERANCE := 0.30   # radians (~18 degrees)

func _ready() -> void:
	visible = false

	if base_material == null:
		base_material = StandardMaterial3D.new()
		base_material.albedo_color = Color.WHITE
		base_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA

	set_surface_override_material(0, base_material)


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


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 with saved stencil data
	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 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:
		rotate_object_local(Vector3.FORWARD, 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:
					var tex_path := tex.resource_path
					_add_stencil_texture(tex, tex_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

	base_material.albedo_texture = collected_textures[current_index]


# ============================================================
# SAVE SYSTEM (Ctrl + P) — append or update active stencil
# ============================================================

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

	if current_index < 0 or current_index >= collected_textures.size():
		print("No active stencil selected, nothing to save.")
		return

	# Read existing save file if it exists
	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)

	# Prepare current stencil entry
	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": {
			"x": global_rotation.x,
			"y": global_rotation.y,
			"z": global_rotation.z
		}
	}

	# Check if this stencil already exists in the file
	var updated := false
	for i in range(save_data.size()):
		if save_data[i]["texture_path"] == entry["texture_path"]:
			save_data[i] = entry  # update existing
			updated = true
			break

	if not updated:
		save_data.append(entry)  # append new stencil

	# Write updated save data back
	_write_json_file(save_data)

	print("Saved current stencil index", current_index, "to:", SAVE_PATH, "(updated:", updated, ")")


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 player_node == null:
		print("ERROR: No player_node assigned.")
		return

	if current_index < 0 or current_index >= collected_texture_paths.size():
		print("No active stencil to compare.")
		return

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

	# Load JSON file
	var file := FileAccess.open(SAVE_PATH, FileAccess.READ)
	if file == null:
		print("Could not open save file.")
		return

	var content := file.get_as_text()
	file.close()

	var parsed = JSON.parse_string(content)
	if parsed == null or not (parsed is Array):
		print("Save file is invalid JSON.")
		return

	var active_path := collected_texture_paths[current_index]

	# Search for matching stencil entry
	for entry in parsed:
		if entry["texture_path"] != active_path:
			print("stencil found")
			continue

		# Extract saved values
		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 saved_stencil_rot := Vector3(
			entry["stencil_rotation"]["x"],
			entry["stencil_rotation"]["y"],
			entry["stencil_rotation"]["z"]
		)

		# Current values
		var current_pos := player_node.global_position
		var current_player_rot := player_node.global_rotation
		var current_stencil_rot := global_rotation

		# Compare differences
		var pos_close := current_pos.distance_to(saved_pos) <= POSITION_TOLERANCE
		var player_rot_close := current_player_rot.distance_to(saved_player_rot) <= ROTATION_TOLERANCE
		var stencil_rot_close := current_stencil_rot.distance_to(saved_stencil_rot) <= ROTATION_TOLERANCE

		# Result
		if pos_close and player_rot_close and stencil_rot_close:
			match_found()
		else:
			print("❌ No match for this stencil.")
			print("Position difference:", current_pos.distance_to(saved_pos))
			print("Player rotation difference:", current_player_rot.distance_to(saved_player_rot))
			print("Stencil rotation difference:", current_stencil_rot.distance_to(saved_stencil_rot))

		return

	print("No saved entry found for current stencil texture.")
	
	
func match_found() -> void:
	print("✅ MATCH! Player + stencil state matches saved data.")
