extends CharacterBody3D

class_name Clown

signal score_event

#const SPEED = 5.0
const JUMP_VELOCITY = 4.5
const TRAMPOLINE_VELOCITY = 9
const FALL_DEATH_HEIGHT = 5
const TRAMPOLINE_FLYING_SPEED = 5
const CANNON_FLYING_SPEED = 6

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
var direction = (transform.basis * Vector3(1, 0, 0)).normalized()

# Cannon stuff
var isInCannon = false
var isShotFlying = false
var time_of_shot = 0

# Ice cube
var isOnIce = false

# Seeswa
var isOnSeesaw = false
var isFlippedFlying = false
var time_of_seesaw = 0

# Trampoline stuff
var isTrampolineFlying = false
var time_of_trampoline = 0

# To save how far he falls
var highest_flight_point = 0
# Is he alive?
var alive = true
var crawling = false
var time_of_death = 0
var rng = RandomNumberGenerator.new()
var death_duration = rng.randi_range(50, 500)

# Tripped over banana?
var isSliding = false
var time_of_banana = 0
var sliding_duration = 250

var personal_speed = rng.randf_range(4, 6)

func _ready():
	GameConstants.get_score_ui_node()._connect_clown(self)

func _physics_process(delta):
	# Add the gravity.
	if isInCannon:
		return

	if not is_on_floor():
		velocity.y -= gravity * delta

		# Store the highest point of flight
		if highest_flight_point < position.y:
			highest_flight_point = position.y
		
		#if ($Clown/AnimationPlayer as AnimationPlayer).current_animation != "A_InAir":
			#($Clown/AnimationPlayer as AnimationPlayer).play("A_InAir")
			#($Clown/AnimationPlayer as AnimationPlayer).speed_scale = 1

	if ($Clown/AnimationPlayer as AnimationPlayer).current_animation == "A_Walk":
		#($Clown/AnimationPlayer as AnimationPlayer).speed_scale = 2.8
		($Clown/AnimationPlayer as AnimationPlayer).speed_scale = personal_speed / 1.2
	elif ($Clown/AnimationPlayer as AnimationPlayer).current_animation == "A_RollOver":
		($Clown/AnimationPlayer as AnimationPlayer).speed_scale = 2
	else:
		($Clown/AnimationPlayer as AnimationPlayer).speed_scale = 1


	if is_on_floor():		
		# When he lands we need to check how much he fell
		if alive and highest_flight_point - position.y > FALL_DEATH_HEIGHT:
			die()

		else:
			highest_flight_point = position.y
			if !($Clown/AnimationPlayer as AnimationPlayer).is_playing() and !isInCannon and !isOnSeesaw and !isOnIce and ($Clown/AnimationPlayer as AnimationPlayer).current_animation != "A_Idle" and !crawling:
				print("start walk")
				($Clown/AnimationPlayer as AnimationPlayer).play("A_Walk")

	# Stop flying when touching floor
	# Need to have a bit of a delay because at start of the shot it is still on floor
	if isShotFlying and Time.get_ticks_msec() - time_of_shot > 20 and is_on_floor():
		isShotFlying = false
		time_of_shot = 0
		velocity.y = 5
		($Clown as Node3D).rotation.x = 0
		($Clown as Node3D).position.y = -1
		($Clown as Node3D).position.x = 0
		($Clown/AnimationPlayer as AnimationPlayer).play("A_RollOver")
		($Clown/AnimationPlayer as AnimationPlayer).animation_set_next("A_RollOver", "A_Walk")

	if isTrampolineFlying and Time.get_ticks_msec() - time_of_trampoline > 20 and is_on_floor():
		isTrampolineFlying = false
		time_of_trampoline = 0
		velocity.y = 3
		($Clown/AnimationPlayer as AnimationPlayer).play("A_RollOver")
		($Clown/AnimationPlayer as AnimationPlayer).animation_set_next("A_RollOver", "A_Walk")

	if isFlippedFlying and Time.get_ticks_msec() - time_of_seesaw > 20 and is_on_floor():
		isFlippedFlying = false
		time_of_seesaw = 0
		velocity.y = 3
		($Clown/AnimationPlayer as AnimationPlayer).play("A_RollOver")
		($Clown/AnimationPlayer as AnimationPlayer).animation_set_next("A_RollOver", "A_Walk")

	if !alive:
		# Rotate a bit after dying
		if Time.get_ticks_msec() - time_of_death < death_duration:
			rotate_z(0.25)

		# If not touching the floor, make sure to fall down
		if !is_on_floor():
			direction = (transform.basis * Vector3(0, 1, 0)).normalized()
			move_and_slide()

	# Slide for specified time and then die
	if isSliding and Time.get_ticks_msec() - time_of_banana > sliding_duration and get_real_velocity().y == 0:
		isSliding = false
		die()

	if isShotFlying:
		pass
		velocity.x = direction.x * CANNON_FLYING_SPEED
	elif isTrampolineFlying:
		pass
		velocity.x = direction.x * TRAMPOLINE_FLYING_SPEED
	# TODO seesawflying constant
	else:
		if direction:
			#print("direction exists")
			velocity.x = direction.x * personal_speed
		else:
			#print("direction no exists")
			velocity.x = move_toward(velocity.x, 0, personal_speed)
	
	#if crawling and ($Clown/AnimationPlayer as AnimationPlayer).current_animation != "A_EmergeFromCar":
	if crawling and !($Clown/AnimationPlayer as AnimationPlayer).is_playing():
		crawling = false
		$Clown.rotation.y = 90
		$Clown.position.y = -1
		direction.x = 1

	if alive and not crawling:
		move_and_slide()

func crawl():
	await get_tree().physics_frame
	await get_tree().physics_frame
	$Clown.rotation.y = 90
	#$Clown.position.y = 0
	($Clown/AnimationPlayer as AnimationPlayer).play("A_EmergeFromCar")
	crawling = true

func die():
	print("Good Bye!")
	alive = false
	time_of_death = Time.get_ticks_msec()
	isShotFlying = false
	isTrampolineFlying = false
	isSliding = false
	$DieSound.play()
	
	($Clown/AnimationPlayer as AnimationPlayer).stop()
	($Clown/AnimationPlayerSalto as AnimationPlayer).stop()
	await get_tree().physics_frame
	($Clown as Node3D).rotation = Vector3.ZERO
	($Clown as Node3D).position = Vector3(0, -1, 0)

	($Clown/ClownRig/Skeleton3D as Skeleton3D).physical_bones_start_simulation(["spina.005"])
	
	score_event.emit("Death")

func _on_wall_detection_body_entered(_body):
	print("Ouch my Nose")
	rotate_y(PI)
	direction = direction * -1;

func _on_object_detection_body_shape_entered(_body_rid, body, body_shape_index, _local_shape_index):
	if body is Trampoline:
		print("boing")
		$TrampolineSound.play()
		velocity.y = TRAMPOLINE_VELOCITY
		isTrampolineFlying = true
		highest_flight_point = 0
		time_of_trampoline = Time.get_ticks_msec()
		emit_signal("score_event", "Trampoline")
		($Clown/AnimationPlayer as AnimationPlayer).play("A_InAir")
		($Clown/AnimationPlayerSalto as AnimationPlayer).play("Pirouette")

	if body is IceCube:
		print("brrrr")
		$IceSound.play(0)
		direction = (transform.basis * Vector3(0, 0, 0)).normalized()
		#($Clown as Node3D).rotation = Vector3(0,25,0).normalized()
		($Clown as Node3D).rotation.y = 3
		isOnIce = true
		#Stop the animation and make him icey
		($Clown/AnimationPlayer as AnimationPlayer).stop()
		($Clown/ClownRig/Skeleton3D/ClownBody as MeshInstance3D).material_overlay = preload("res://mechanics/clown/clownIce.tres")
		($Clown/ClownRig/Skeleton3D/ClownHair as MeshInstance3D).material_overlay = preload("res://mechanics/clown/clownIce.tres")
		($Clown/ClownRig/Skeleton3D/ClownBody as MeshInstance3D).transparency = 0.5

		(body.get_node("iceCreamCup/Cylinder") as MeshInstance3D).transparency = 0.5
		(body.get_node("iceCreamCup/Sphere") as MeshInstance3D).transparency = 1
		(body.get_node("iceCreamCup/Sphere_001") as MeshInstance3D).transparency = 1
		(body.get_node("iceCreamCup/Sphere_002") as MeshInstance3D).transparency = 1
		(body.get_node("iceCreamCup/Sphere_003") as MeshInstance3D).transparency = 1

		#Make him a wall
		(self as CharacterBody3D).set_collision_layer_value(1, false)
		(self as CharacterBody3D).set_collision_layer_value(2, true)
		
		emit_signal("score_event", "Ice")

	if body is Cannon:
		isInCannon = body.tryToEnterCannon()
		if isInCannon:
			($Clown/AnimationPlayer as AnimationPlayer).play("A_Idle")
			body.shoot_clown.connect(_on_clown_shoot)
			var fly_left = body.faces_left
			var facing_left = direction.x < 0
			if fly_left:
				direction = Vector3(-1, 0, 0)
				($Clown as Node3D).position.y = 0.9
				if !facing_left:
					rotate_y(PI)
				else:
					($Clown as Node3D).position.x = 1

				($Clown as Node3D).rotation.x = 1.5
			else:
				direction = Vector3(1, 0, 0)
				($Clown as Node3D).position.y = 0.9
				if facing_left:
					rotate_y(PI)
				else:
					($Clown as Node3D).position.x = 1
				($Clown as Node3D).rotation.x = 1.5

	if body is Banana:
		print("Weeeeeee")
		($Clown/AnimationPlayer as AnimationPlayer).play("A_SlipOnPeel")
		start_sliding()
		# Use Banana only once
		body.queue_free()
		$SeesawSound.play()
		emit_signal("score_event", "Banana")

	if body is Clown and isSliding and !body.isSliding and body.alive:
		print("touchy touchy")
		body.start_sliding()
		emit_signal("score_event", "SlideInto")
		body.get_node("Clown/AnimationPlayer").play("A_SlipOnPeel")

	if body is Clown and isShotFlying and body.alive:
		print("ouch")
		velocity.y = 8
		body.die()
		die()
		emit_signal("score_event", "ShotInto")

	if body is Seesaw:
		if is_on_floor():
			if (body_shape_index and body.tryToSitLeft()) or (!body_shape_index and body.tryToSitRight()):
				print("Ahhh")
				($Clown/AnimationPlayer as AnimationPlayer).play("A_Idle")
				direction = (transform.basis * Vector3(0, 0, 0)).normalized()
				isOnSeesaw = true
				body.flip_clown.connect(_on_clown_flip)
				#TODO emit_signal("score_event", "SeesawSit")
		else:
			if (body_shape_index and body.tryToFlipLeft()) or (!body_shape_index and body.tryToFlipRight()):
				print("Hepp")

func start_sliding():
		isSliding = true
		time_of_banana = Time.get_ticks_msec()

		personal_speed = personal_speed * 1.2
		($Clown/AnimationPlayer as AnimationPlayer).play("A_SlipOnPeel")

		# if he is walking up, he needs to change direction to slide down
		if get_real_velocity().y > 0:
			direction *= -1

		# Rotate a bit depending on move direction
		if direction.x < 0:
			#rotate_y(PI)
			pass
			#$Clown.rotation.x = 1
			#if get_real_velocity().y > 0:
				#$Clown.rotation.x = -1
		else:
			#rotate_y(PI)
			pass
			#$Clown.rotation.x = -1
			#if get_real_velocity().y > 0:
				#$Clown.rotation.x = 1

func _on_clown_flip(force, fly_left):
	print("Wohiiiiiii")
	var facing_left = direction.x < 0
	#print("fly_left: ", fly_left, "; face_left: ", facing_left)
	($Clown/AnimationPlayer as AnimationPlayer).play("A_InAir")
	($Clown/AnimationPlayerSalto as AnimationPlayer).play("Salto")
	if fly_left:
		#print("fly_left")
		direction.x = -1
		if !facing_left:
			rotate_y(PI)
	if !fly_left:
		#print("fly_right")
		direction.x = 1
		if facing_left:
			rotate_y(PI)
	isOnSeesaw = false
	isFlippedFlying = true
	time_of_seesaw = Time.get_ticks_msec()
	velocity.y = force
	$SeesawSound.play()
	emit_signal("score_event", "SeesawFlip")

func _on_clown_shoot(force, fly_left):
	print("Whoooooooo")
	$CannonSound.play(0.06)
	($Clown/AnimationPlayer as AnimationPlayer).play("A_InAir")
	if fly_left:
		direction = Vector3(-1, 0, 0)
	else:
		direction = Vector3(1, 0, 0)
	isInCannon = false
	isShotFlying = true
	time_of_shot = Time.get_ticks_msec()
	velocity.y = force
	emit_signal("score_event", "Cannon")
