extends RigidBody2D


var held = false
var originalPos # Position before moving the ship
var snapOriginalPos = false # Gets the original position
var mousePos # Stores the last known mouse position so the physics engine can use it
var vertical = true # Gets ship which is either vertical or horizonal
var startingPos # Starting position of ships before being placed

# Ships are all named starting with their length,
# So we cast from string to int, on the ship name, and get the length
onready var ship_length = int(name)

# This is set when we're colliding with something
var collision = false

# Called when the node enters the scene tree for the first time.
func _ready():
	mode = MODE_KINEMATIC
	contacts_reported = 64
	set_use_custom_integrator(true)
	# Snap the ships to the grid, so the engine won't get mad when they're moved away from the starting position every frame
	position = (position - offset).snapped(Vector2(32, 32)) + offset
	startingPos = position
	var _trash
	# Connect to my own signals, and not the signals of my fellowships
	# PLEASE don't parameterize; there's no way to tell these signals apart with the args the engine provides.
	_trash = connect("body_entered", self, "ship_stacked")
	_trash = connect("body_exited",  self, "ship_unstacked")

# Radius of the "knob" on the center of each ship
var click_radius = 16

func _input(event):
	if event is InputEventMouseButton and event.button_index == BUTTON_LEFT:	
		if (event.position - position).length() < click_radius:
			if not held and event.pressed:
				AudioBus.emit_signal("button_clicked")
				pickup()
				
		if held and not event.pressed:
			drop()
			# Convert the center of this piece to board-space
			var bs_position = world_to_board_space(position)
			# Check whether the piece is within half a board-space of the grid (-0.5, 9.5)
			if not (bs_position.x > -0.5 and bs_position.x < 9.5 and bs_position.y > -0.5 and bs_position.y < 9.5):
				if originalPos != null:
					collision = true
					vertical = true

	if event is InputEventMouseMotion and held:
		if snapOriginalPos == false:
			originalPos = position
			snapOriginalPos = true
		# Save the moise position, so _physics_process can use it
		mousePos = event.position;
		
	if event.is_action_pressed("ui_rotate"):
		if not held and not checkOriginalPos():
			if(event.position - position).length() < click_radius:
				#Play a sound
				AudioBus.emit_signal("button_clicked")
				# Rotation has been moved to _physics_process,
				# as per recommendation of godot_engine.org
				vertical = not vertical
				# It takes 3 physics ticks for the rotation to update the hitboxes
				# Determined imperically through testing, may work differently on different machines(?)
				released = 3

# Offset from the corner of the screen to the corner of the board
const offset = Vector2(36, 36)
# The previous verticality of the object
var   prev_vertical = true
# The previous position of the object
var   prev_position = Vector2(0,0)
# The number of frames after an object is released to check for physics updates
var   released = 0


#   _physics_process: called in place of the physics processor
#     Checks collision and updates the position and rotation of the object
func _physics_process(_delta):
	# calculate whether the piece has been rotated or moved
	var rotated = prev_vertical != vertical
	var moved = prev_position != position

	# If the piece is held, move it to the mouse:
	if held and mousePos and mousePos != position:
		position = mousePos
		mousePos = null

	# Snap it to the grid if not held (and previously moved)
	if not held and moved:
		position = (position - offset).snapped(Vector2(32, 32)) + offset
		prev_position = position

	# If it's been moved or rotated, snap it to the board
	if released or rotated:
		# check whether the ends of the piece are within the board
		var linear_move = check_extents(position, vertical, ship_length)
		# if not, move them back inside
		if linear_move:
			if vertical:
				position += 32 * Vector2(0, linear_move)
			else:
				position += 32 * Vector2(linear_move, 0)
			pass

	# Check collisions after released, reset if colliding
	if collision and released:
		position = startingPos
		rotation = 0
		vertical = true

	# Rotate if the piece needs to be rotated
	if rotated:
		prev_vertical = vertical
		rotation = -PI/2 * int(not vertical) # int(true) == 1, int(false) == 0

	# Count down the number of physics timesteps left until the piece can stop processing
	if released > 0: 
		released = released - 1


func pickup():
	if not held:
		raise() # Render this ship on top of other ships
		held = true # mark it as held
		collision = false # Assume we're not colliding by default

func drop():
	if held:
		released = 1 # mark the node as released
		held = false # mark the node as not held
		snapOriginalPos = false

func checkOriginalPos():
	return position == startingPos


# Called when *this* ship collides with another ship
func ship_stacked(_body):
	collision = true
# Called when *this* ship stops colliding with another ship
func ship_unstacked(_body):
	collision = false

# Calculate the extents (front to back) of the ship and check whether they're on the board
# Returns how many squares to move the ship along its orientation axis (positive or negative)
func check_extents(center, orientation, length):
	center = world_to_board_space(center) # Convert to board-space (0-10)
	# Calculate the position of the front of the ship
	# Orientation is true when the ship is vertical
	var bow   = vectorget(center, orientation) - floor((length - 1) / 2)
	# if out of bounds, return how much to move the ship by
	if bow < 0:
		return -bow
	# Calculate the position of the rear of the ship
	var stern = vectorget(center, orientation) + floor(length / 2)
	# If out of bounds, return how much to move the ship by
	if stern >= 10:
		return -(stern - 9)
	return 0

func validate_placement():
	# Checks whether the ship's center is on the board.
	# As long as the ship was moved according to the rules defined in the
	# _physics_process function, this should be necessary and sufficient
	# to say the ship is on the board
	return check_extents(position, false, 1)

func clear():
	# ships return home on collision
	# simulate a collision
	collision = true
	released = 1

# Convert the world-space coordinates to positions on the board
func world_to_board_space(vector):
	# Do math
	var res = (vector - offset) / 32 # Basically Fahrenheit/Celcius conversion, but in 2D
	return res

# Inverse of the above function.
func board_to_world_space(vector):
	# Do math
	var res = (vector * 32) + offset #Invert the above function
	return res #Truncate decimals

# index a Vector2 like an array
# Why is this needed?
# So we can discard the unimportant axis! (a ship is always 1 unit wide!)
func vectorget(vector, axis):
	if axis:
		return vector.y
	else:
		return vector.x
