class_name Bomb extends Node3D

# TODO: have those in item db instead ?
@export var _splash_small_sound: AudioStreamPlayer3D
@export var _splash_small_quiet_sound: AudioStreamPlayer3D
@export var _sell_sound: AudioStreamPlayer3D
@export var _model: Node3D
@export var _splash_particles: GPUParticles3D
@export var _sell_particles: GPUParticles3D
@export var _picking_area: Area3D
@export var body: RigidBody3D
@export var billboard := false

## Initials to construct from
@export var item_id := &"ID"
@export var item_count := 0

## Will be constructed as { item_id, item_count }, but could be edited after
## Use set_item_bundle() if it's dynamically assigned, after adding to the scene
@export var item_bundle: Dictionary

## Effect of splhashing.
@export_enum("none", "water") var splash_function := "none"

## no longer exists and shouldn't be considered, but not ready to be freed yet
var is_dead := false

## something to ignore
var sender_id: int
var sender_body: PhysicsBody3D

var _in_splash_range := {}


func _ready() -> void:
	body.process_mode = Node.PROCESS_MODE_DISABLED


func _enter_tree() -> void:
	if item_bundle.is_empty():
		item_bundle = {
			"item_id": item_id,
			"count": item_count
		}


@rpc("authority", "call_local", "reliable")
func set_item_bundle(p_item_bundle: Dictionary) -> void:
	item_bundle = p_item_bundle
	if _model.has_method("reflect_bundle"):
		_model.reflect_bundle(item_bundle)


func _process(delta: float) -> void:
	# spin around, no need to replicate this
	if not billboard:
		_model.basis = _model.basis.rotated(Vector3(1, 0, 0), -((TAU*2) * delta))
		_model.basis = _model.basis.orthonormalized()
	
	if not is_multiplayer_authority():
		return
	
	# TODO: interpolate for non-authority clients
	#		or, do we *really* care about synchronicity? could simulate it independently.
	global_position = body.global_position


@rpc("authority", "call_local", "reliable")
func _disable_body() -> void:
	# not using synchronizer for this because it ends up enabling processing
	# on other clients
	(func() -> void: body.process_mode = Node.PROCESS_MODE_DISABLED).call_deferred()
	_picking_area.collision_layer = 0


func _on_body_entered(p_body: Node3D) -> void:
	if p_body == sender_body:
		return

	if p_body.is_in_group("voids"):
		queue_free()
		return
	
	if p_body.is_in_group("sell_boxes"):
		var item := GameState.fetch().INVENTORY_ITEM_DB[item_bundle["item_id"]] as InventoryItem
		if item.sells_for != 0:
			get_node("/root/Main").add_inventory_item.rpc(&"coin", item.sells_for * item_bundle["count"])
			if _sell_sound != null:
				_sell_sound.play()
				if _sell_particles != null:
					_sell_particles.amount =  item.sells_for * item_bundle["count"]
					_sell_particles.emitting = true
				is_dead = true
				_model.hide()
				_disable_body.rpc()
				await _sell_particles.finished
			queue_free()
		return
	
	if splash_function == "water":
		for area: Area3D in _in_splash_range:
			area.get_parent_node_3d().water.rpc_id(1, sender_id)
	
	if _splash_small_sound != null and sender_id != 0:
		_splash_small_sound.play()
	if _splash_small_quiet_sound:
		_splash_small_quiet_sound.play()
	if _splash_particles:
		_splash_particles.emitting = true
	
	is_dead = true
	_model.hide()
	_disable_body.rpc()

	if _splash_small_sound != null and sender_id != 0:
		await _splash_small_sound.finished
	if _splash_small_quiet_sound != null and _splash_small_quiet_sound.playing:
		await _splash_small_quiet_sound.finished

	queue_free()


func _on_splash_area_entered(area: Area3D) -> void:
	_in_splash_range[area] = true


func _on_splash_area_exited(area: Area3D) -> void:
	_in_splash_range.erase(area)


@rpc("authority", "call_local", "reliable")
func set_global_pos(new: Vector3) -> void:
	global_position = new
	body.global_position = new
	body.reset_physics_interpolation()


@rpc("any_peer", "call_local", "reliable")
func get_picked_up() -> void:
	if is_multiplayer_authority():
		queue_free()


func mark_interactive() -> void:
	# TODO: whatever
	if _model.scene_file_path == "res://assets/water-bomb.glb":
		for submodel in _model.get_children():
			(submodel as MeshInstance3D).mesh.surface_get_material(0).next_pass.set("shader_parameter/color", Color.WHITE)
	elif _model.scene_file_path == "res://src/ingame/quad_viewmodel.tscn":
		_model.mesh.material_override.set("shader_parameter/outline_color", Color.WHITE)


func mark_non_interactive() -> void:
	if _model.scene_file_path == "res://assets/water-bomb.glb":
		for submodel in _model.get_children():
			(submodel as MeshInstance3D).mesh.surface_get_material(0).next_pass.set("shader_parameter/color", Color(1, 1, 1, 0))
	elif _model.scene_file_path == "res://src/ingame/quad_viewmodel.tscn":
		_model.mesh.material_override.set("shader_parameter/outline_color", Color.BLACK)
