extends Node # Networking functionality

var player

var is_placing = false
var placing_node
var placed = []
var max_placed = 5

var start_action
var confirm_action
var delete_action

var scene

signal start_placement
signal confirm_placement

func _init(parent, scene_path):
	player = parent
	player.add_child(self)
	# Set the network master to the player's network master, which happens to be its name
	# This allows it to use master, slave keywords appropriately
	var net_id = int(player.get_name())
	set_name("Placement") # We need to share common name for networking
	set_network_master(net_id)
	scene = load(scene_path)
	rpc("request_placed")

master func request_placed():
	for node in placed:
		rpc_id(get_tree().get_rpc_sender_id(), "slave_place", node.transform)

func place_input(radius=-1, can_build=true, require_ghost=false):

	# We allow you to just click to place, without needing to press E
	var confirm = Input.is_action_just_pressed(confirm_action)

	if can_build and Input.is_action_just_pressed(start_action) or (confirm and not is_placing and not require_ghost):
		# Press button twice to cancel
		if is_placing:
			# We changed our mind, delete the placing wall
			placing_node.queue_free()
			is_placing = false
		else:
			# Make a floating placement wall
			placing_node = create()
			is_placing = true

	if Input.is_action_just_pressed(delete_action):
		var pick = player.pick_from(placed)
		if pick != -1:
			rpc("remove_placed", placed[pick].get_name())

	if is_placing:
		position_placement(placing_node)
		if radius > 0: # A radius is specified
			var distance = placing_node.get_translation() - player.get_translation()
			if distance.length() > radius:
				placing_node.out_of_range()
				confirm = false
			else:
				placing_node.within_range()

	if can_build and (confirm and not require_ghost) or (confirm and is_placing):
		# Order matters here: confirm_placement resets placing_node so we have to do anything with it first
		rpc("slave_place", placing_node.transform)
		confirm_placement(placing_node)
		return true
	return false

func confirm_placement(node, tf=null):
	if tf:
		node.set_transform(tf)
	node.place()
	placed.append(node)
	check_count()
	placing_node = null
	is_placing = false

func check_count():
	# If we've made max_walls, remove the first we made
	if placed.size() > max_placed:
		placed[0].queue_free()
		placed.pop_front()
	# When placing, make the about-to-disappear wall translucent
	if placed.size() >= max_placed:
		placed[0].make_last()

# Find the point we're looking at, and put the wall there
func position_placement(node):
	var aim = player.get_node("Yaw/Pitch").get_global_transform().basis
	var look_ray = player.get_node("TPCamera/Camera/Ray")
	var pos = look_ray.get_collision_point()
	node.set_translation(pos)
	var normal = look_ray.get_collision_normal()
	var towards = normal + pos
	var up = aim[2] # Wall should be horizontal to my view
	# This helps nearly horizontal walls be easier to make flat
	# We have two methods I'm deciding between
	var use_method_two = true
	if not use_method_two:
		# Method one: only allow horizontal or vertical, based on whether the surface faces you
		var on_wall_margin = 0.75
		if normal.dot(Vector3(0,1,0)) < on_wall_margin:
			var margin = 0.8
			if up.dot(normal) > margin: # The wall is facing us
				# We want flat
				up = Vector3(0,1,0)
			else:
				# We want straight
				up.y = 0
	else:
		# Method two: Make y more aggressive than other dimensions
		up.y = 3 * tanh(up.y)
	up = up.normalized()
	node.look_at(towards, up)

func clear():
	for node in placed:
		node.queue_free()
	placed = []

slave func slave_place(tf):
	var node = create()
	confirm_placement(node, tf)

sync func remove_placed(name):
	var what = get_node("/root/Level").get_node(name)
	placed.erase(what)
	what.queue_free()

func create():
	var node = scene.instance()
	player.get_node("/root/Level").add_child(node)
	node.init(player)
	return node