class_name LiquidLayer extends Area3D

@export var buoyancy_factor: float = 10.0
@export var lateral_damping_factor: float = 0.0
@export var vertical_damping_factor: float = 0.0

var buoyancy_dict := {}


func _init() -> void:
	body_shape_entered.connect(_on_body_shape_entered)
	body_shape_exited.connect(_on_body_shape_exited)


func _on_body_shape_entered(body_id, body: Node, body_shape_idx: int, self_shape_idx: int) -> void:
	if not body is RigidBody3D:
		return

	var self_collision_shape = shape_owner_get_owner(shape_find_owner(self_shape_idx))
	var body_collision_shape = body.shape_owner_get_owner(body.shape_find_owner(body_shape_idx))

	var self_shape = self_collision_shape.get_shape()
	var body_shape = body_collision_shape.get_shape()

	var self_aabb = create_shape_aabb(self_shape)
	var body_aabb = create_shape_aabb(body_shape)

	buoyancy_dict[body] = {
		"entry_point": body.global_transform.origin, "self_aabb": self_aabb, "body_aabb": body_aabb
	}


func _on_body_shape_exited(body_id, body: Node, body_shape_idx: int, self_shape_idx: int) -> void:
	if body in buoyancy_dict:
		buoyancy_dict.erase(body)


func create_shape_aabb(shape: Shape3D) -> AABB:
	if shape is ConvexPolygonShape3D:
		return create_convex_aabb(shape)
	elif shape is SphereShape3D:
		return create_sphere_aabb(shape)

	return AABB()


func create_convex_aabb(convex_shape: ConvexPolygonShape3D) -> AABB:
	var points = convex_shape.get_points()
	var aabb = null

	for point in points:
		if not aabb:
			aabb = AABB(point, Vector3.ZERO)
		else:
			aabb = aabb.expand(point)

	return aabb


func create_sphere_aabb(sphere_shape: SphereShape3D) -> AABB:
	return AABB(-Vector3.ONE * sphere_shape.radius, Vector3.ONE * sphere_shape.radius)


func _physics_process(delta: float) -> void:
	for body in buoyancy_dict:
		var buoyancy_data = buoyancy_dict[body]

		var self_aabb = buoyancy_data["self_aabb"]
		self_aabb.position += global_transform.origin

		var body_aabb = buoyancy_data["body_aabb"]
		body_aabb.position += body.global_transform.origin

		var displacement = self_aabb.end.y - body_aabb.position.y
		body.add_central_force(Vector3.UP * displacement * buoyancy_factor)
		body.add_central_force(
			Vector3(1, 0, 1) * body.get_linear_velocity() * displacement * -lateral_damping_factor
		)
		body.add_central_force(
			Vector3(0, 1, 0) * body.get_linear_velocity() * -vertical_damping_factor
		)
