extends CharacterBody3D

# Movement settings
@export var speed: float = 5.0
@export var jump_force: float = 10.0
@export var gravity: float = 20.0
@export var push_force: float = 10.0  # Force applied to RigidBody3D
@export var mouse_sensitivity: float = 0.002  # Mouse sensitivity
@export var max_look_angle: float = 90.0  # Maximum vertical look angle
@export var interaction_range: float = 3.0  # Range for interacting with objects
@export var interaction_strenght: int = 10

# Ghost mode settings
@export var ghost_mode: bool = false  # Toggle ghost mode
@export var ghost_speed: float = 10.0  # Movement speed in ghost mode

# Collision layers
@export var normal_collision_layer: int = 1  # Layer 1: Normal
@export var ghost_collision_layer: int = 2   # Layer 2: Ghost

# Light flicker settings
@export var flicker_interval: float = 0.1  # Time between flickers
@export var flicker_intensity: float = 0.5  # Intensity of flicker (0 to 1)
@export var flicker_range: float = 10.0  # Range within which lights flicker
var flicker_timer: float = 0.0

func _ready() -> void:
	# Capture the mouse so it doesn't leave the game window
	Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
	# Initialize collision layer based on starting mode
	update_collision_layer()

func _input(event: InputEvent) -> void:
	if event is InputEventMouseMotion:
		# Rotate the player horizontally (yaw)
		rotate_y(-event.relative.x * mouse_sensitivity)

		# Rotate the camera vertically (pitch)
		$Camera3D.rotate_x(-event.relative.y * mouse_sensitivity)
		$Camera3D.rotation_degrees.x = clamp($Camera3D.rotation_degrees.x, -max_look_angle, max_look_angle)

	# Check for interaction input (e.g., pressing "E")
	if event.is_action_pressed("interact"):
		interact_with_object()

	# Toggle ghost mode (e.g., pressing "G")
	if event.is_action_pressed("toggle_ghost_mode"):
		if ghost_mode: 
			interaction_strenght=10
		else: 
			interaction_strenght=50
		ghost_mode = !ghost_mode
		print("Ghost mode: ", ghost_mode)
		# Update collision layer when toggling ghost mode
		update_collision_layer()

func update_collision_layer() -> void:
	if ghost_mode:
		# Set collision layer and mask for ghost mode
		collision_layer = ghost_collision_layer
		collision_mask = ghost_collision_layer
	else:
		# Set collision layer and mask for normal mode
		collision_layer = normal_collision_layer
		collision_mask = normal_collision_layer

func _physics_process(delta: float) -> void:
	if ghost_mode:
		ghost_movement(delta)
		flicker_lights(delta)
	else:
		normal_movement(delta)

func normal_movement(delta: float) -> void:
	var input_direction: Vector3 = Vector3.ZERO

	# Get input for movement
	if Input.is_action_pressed("move_forward"):
		input_direction.z -= 1
	if Input.is_action_pressed("move_backward"):
		input_direction.z += 1
	if Input.is_action_pressed("move_left"):
		input_direction.x -= 1
	if Input.is_action_pressed("move_right"):
		input_direction.x += 1

	# Normalize input to prevent faster diagonal movement
	input_direction = input_direction.normalized()

	# Apply movement in the camera's direction
	var direction: Vector3 = (transform.basis * input_direction).normalized()
	velocity.x = direction.x * speed
	velocity.z = direction.z * speed

	# Apply gravity
	if not is_on_floor():
		velocity.y -= gravity * delta

	# Jumping
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = jump_force

	# Move the player and detect collisions
	move_and_slide()

	# Apply force to RigidBody3D objects on collision
	for index in range(get_slide_collision_count()):
		var collision = get_slide_collision(index)
		var collider = collision.get_collider()

		if collider is RigidBody3D:
			# Calculate the direction and force to apply
			var force_direction = -collision.get_normal()
			var force = force_direction * push_force

			# Apply the force to the RigidBody3D
			collider.apply_central_impulse(force)

func ghost_movement(delta: float) -> void:
	var input_direction: Vector3 = Vector3.ZERO

	# Get input for movement
	if Input.is_action_pressed("move_forward"):
		input_direction.z -= 1  # Forward
	if Input.is_action_pressed("move_backward"):
		input_direction.z += 1  # Backward
	if Input.is_action_pressed("move_left"):
		input_direction.x -= 1  # Left
	if Input.is_action_pressed("move_right"):
		input_direction.x += 1  # Right

	# Normalize input to prevent faster diagonal movement
	input_direction = input_direction.normalized()

	# Calculate movement direction relative to the camera
	var camera_basis = $Camera3D.global_transform.basis
	var direction: Vector3 = (
		camera_basis.z * input_direction.z +  # Forward/backward
		camera_basis.x * input_direction.x    # Left/right
	).normalized()

	# Apply movement
	velocity = direction * ghost_speed

	# Move the player
	move_and_slide()

func flicker_lights(delta: float) -> void:
	flicker_timer += delta
	if flicker_timer >= flicker_interval:
		flicker_timer = 0.0
		# Get all lights in the "flicker_lights" group
		var lights = get_tree().get_nodes_in_group("flicker_lights")
		for light in lights:
			if light is OmniLight3D or light is SpotLight3D:
				# Check if the light is within range
				if light.global_transform.origin.distance_to(global_transform.origin) <= flicker_range:
					# Randomly adjust the light's energy
					light.light_energy = randf_range(1.0 - flicker_intensity, 1.0 + flicker_intensity)

func interact_with_object() -> void:
	# Raycast to check for interactable objects in front of the player
	var raycast = $Camera3D/RayCast3D
	if raycast.is_colliding():
		var collider = raycast.get_collider()

		if collider is RigidBody3D:
			# Apply a force or torque to flip the object
			collider.freeze = false
			var flip_force = Vector3(0, interaction_strenght, 0)  # Force to flip the object upward
			var flip_torque = Vector3(interaction_strenght, 0, 0)  # Torque to rotate the object
			collider.apply_central_impulse(flip_force)
			collider.apply_torque_impulse(flip_torque)
