extends Control
class_name Taquin
tool

signal solved

var Piece = preload("res://src/Piece.tscn")

export var rows: int = 4
export var columns: int = 4
export var width: int = 512
export var height: int = 512
export var difficulty: int = 10

var interpiece: int = 4
var padding = 15

var pieces: Array = []
var missing_piece: Vector2
var rng = RandomNumberGenerator.new()

var current_animation_path: String = "AnimationPlayer/MockPiece:position"

var swipe = Vector2(0, 0)

func position_for_index(index: Vector2, size: int) -> Vector2:
	return Vector2(padding + index.x * (size + interpiece), padding + index.y * (size + interpiece))
	

func _ready() -> void:
	$AnimationPlayer/MockPiece.visible = false

	$Background.rect_size.x = width
	$Background.rect_size.y = height
	
	rng.randomize()
	var piece_size: int = compute_piece_size()

	for c in range(columns):
		var pieces_row: Array = []
		for r in range(rows):
			var piece = Piece.instance()

			piece.size = piece_size
			piece.position = position_for_index(Vector2(c, r), piece.size)
			piece.number = 1 + c + r * columns
			piece.piece_scale = Vector2((float(piece_size) / width), (float(piece_size) / height))
			piece.taquin_position = Vector2(float(piece.position.x) / width, float(piece.position.y) / height)

			if r == rows - 1 && c == columns - 1:
				piece.visible = false
				missing_piece.x = c
				missing_piece.y = r

			$Background.add_child(piece)
			pieces_row.append(piece)

		pieces.append(pieces_row)

	shuffle(difficulty)

func compute_piece_size() -> int:
	var w_size: int = (width - (2 * padding) - ((columns - 1) * interpiece)) / columns
	var h_size: int = (height - (2 * padding) - ((rows - 1) * interpiece)) / rows
	return int(min(w_size, h_size))

func _input(event):
	if $AnimationPlayer.is_playing():
		# Disable input during animation
		return

	if event.is_action_pressed("ui_up"):
		move_piece(Direction.DOWN)
	if event.is_action_pressed("ui_down"):
		move_piece(Direction.UP)
	if event.is_action_pressed("ui_left"):
		move_piece(Direction.RIGHT)
	if event.is_action_pressed("ui_right"):
		move_piece(Direction.LEFT)

	if event is InputEventScreenDrag:
		swipe = event.relative
	if event is InputEventScreenTouch:
		if not event.pressed: # Touch released
			var angle = swipe.angle()
			if angle < PI / 4 and angle >= - PI / 4:
				move_piece(Direction.LEFT)
			if angle >= PI / 4 and angle < PI - PI / 4:
				move_piece(Direction.UP)
			if angle >= - PI + PI / 4 and angle < - PI / 4:
				move_piece(Direction.DOWN)
			if angle >= PI - PI / 4 or angle < -PI + PI / 4:
				move_piece(Direction.RIGHT)

enum Direction { UP, DOWN, LEFT, RIGHT }

func move_piece(direction) -> bool:
	var destination: Vector2 = missing_piece
	match direction:
		Direction.UP:
#			print("up")
			destination.y -= 1
		Direction.DOWN:
			destination.y += 1
#			print("down")
		Direction.LEFT:
			destination.x -= 1
#			print("left")
		Direction.RIGHT:
			destination.x += 1
#			print("right")
	
#	print(destination)
	if (destination.x < 0 || destination.x >= columns
	|| destination.y < 0 || destination.y >= rows):
		print("impossible move")
		return false

	var moving_piece: Piece = pieces[destination.x][destination.y]
	var moving_piece_animation: Animation = $AnimationPlayer.get_animation("MovingPiece")

	assert(moving_piece_animation != null)
	assert(moving_piece_animation.get_track_count() > 0)

	var moving_piece_track_index: int = moving_piece_animation.find_track(current_animation_path)

	assert(moving_piece_track_index != -1)

	var new_animation_path: String = str($AnimationPlayer.get_parent().get_path_to(moving_piece), ":position")
	moving_piece_animation.track_set_path(moving_piece_track_index, new_animation_path)
	current_animation_path = new_animation_path
	
	moving_piece_animation.track_set_key_value(moving_piece_track_index, 0, position_for_index(destination, moving_piece.size))
	moving_piece_animation.track_set_key_value(moving_piece_track_index, 1, position_for_index(missing_piece, moving_piece.size))
	$AnimationPlayer.play("MovingPiece")

	swap(missing_piece, destination)
	missing_piece = destination
	
	update()
	if check_solved():
		emit_signal("solved")
	
	return true

func swap(src: Vector2, dst: Vector2) -> void:
	var tmp: Piece = pieces[src.x][src.y]
	pieces[src.x][src.y] = pieces[dst.x][dst.y]
	pieces[dst.x][dst.y] = tmp
#	pieces[src.x][src.y].position = position_for_index(src, tmp.size)
#	pieces[dst.x][dst.y].position = position_for_index(dst, tmp.size)

func shuffle(count: int) -> void:
	while count > 0:
		count -= 1
		var direction = rng.randi_range(Direction.UP, Direction.RIGHT)
		move_piece(direction)

func check_solved() -> bool:
	for c in range(columns):
		for r in range(rows):
			if pieces[c][r].number != 1 + c + r * columns:
				return false
	return true