extends Node3D
class_name PlaceableManager

@onready var camera: Camera3D = $"../Camera3D"
const place_effect: PackedScene = preload("res://mechanics/environment/placeable/place_effect/place_effect.tscn")

var place_mode: bool = false
var placeable: PlaceableData
var current_placeable_ui: PlaceableUI
var preview_node: PlaceableBehaviour
var current_mouse_pos_valid: bool = true


func _process(_delta: float):
	if place_mode:
		var target_position: Vector3 = get_mouse_target_position()
		
		
		var mouse_y_valid: bool = mouse_y_is_valid()
		
		preview_node.additional_factor_valid = current_mouse_pos_valid and mouse_y_valid
		preview_node.recalculate_valid()
		preview_node.global_position = target_position
		if Input.is_action_just_pressed("place_confirm") and mouse_y_valid:
			if preview_node.valid_placement:
				current_placeable_ui.quantity -= 1
				place_mode = false
				preview_node.deactivate_preview()
				preview_node = null
				var instance_effect: Node = place_effect.instantiate()
				self.add_child(instance_effect)
				instance_effect.global_position = target_position
				instance_effect.activate()
				(get_viewport().get_camera_3d() as CameraController).shake(0.2, 5)
		elif Input.is_action_just_pressed("place_cancel"):
			if is_instance_valid(preview_node):
				preview_node.queue_free()
				place_mode = false
				preview_node = null

## Returns if the current mouse y position is valid (because if mouse is at bottom of the screen (over buttons) it should not be valid)
func mouse_y_is_valid() -> bool:
	var window_size: Vector2i = get_viewport().get_window().size
	var mouse_pos: Vector2 = get_viewport().get_mouse_position()
	#print("viewport = ", window_size, "  mouse pos = ", get_viewport().get_mouse_position())
	
	var safe_pixels: int = 64
	if mouse_pos.y > (window_size.y - safe_pixels):
		return false
	
	return true

func activate_place_mode(placeable_ui: PlaceableUI):
	current_placeable_ui = placeable_ui
	var to_be_placed_placeable: PlaceableData = placeable_ui.placeable_item
	
	if place_mode:
		# Already in place mode, clean up previous place mode preview
		if is_instance_valid(preview_node):
			preview_node.queue_free()
	
	place_mode = true
	placeable = to_be_placed_placeable
	
	var instantiated: Node = placeable.scene.instantiate()
	if not instantiated is PlaceableBehaviour:
		push_error("Placeable.scene is not of type PlaceableBehaviour")
		breakpoint
	preview_node = instantiated
	self.add_child(preview_node, true)
	preview_node.activate_preview()

## Returns the position where the mouse is pointing at on the Z_COORD plane
func get_mouse_target_position(grounded: bool = true) -> Vector3:
	current_mouse_pos_valid = true
	const Z_COORD: float = 0
	# Get 3D Target position of mouse
	var mouse_coords: Vector2 = get_viewport().get_mouse_position()
	var z_plane  = Plane(Vector3(0, 0, 1), Z_COORD)
	var target_position: Vector3 = z_plane.intersects_ray(
		camera.project_ray_origin(mouse_coords),
		camera.project_ray_normal(mouse_coords))
	
	# Check if we are colliding with something at target_position
	var space_state = get_world_3d().direct_space_state
	var col_mask: int = 0xFFFFFFFF - 8 - 32 # Every mask layer except for layer 4 and layer 6
	var query = PhysicsRayQueryParameters3D.create(camera.global_position, target_position, col_mask)
	var result: Dictionary = space_state.intersect_ray(query)
	if result:
		target_position = result.position
		target_position.z = Z_COORD
		var angle: float = abs(Vector3.UP.angle_to(result.normal))
		if angle > PI/4:
			current_mouse_pos_valid = false
	elif grounded:
		# not colliding but should be grounded => cast raycast downwards
		query = PhysicsRayQueryParameters3D.create(target_position, target_position + Vector3.DOWN * 100, col_mask)
		result = space_state.intersect_ray(query)
		if result:
			target_position = result.position
			target_position.z = Z_COORD
		else:
			# Invalid position (in the air)
			current_mouse_pos_valid = false
	return target_position
