class_name Whale
extends KinematicBody

enum { IDLE, WALKING }

export (NodePath) var _camera_path: NodePath


var _state: = IDLE
var _velocity: = Vector3()
var _max_speed: = 3.0
var _anglar_speed: = PI * 2.0;
var _base_speed: = 3.0
var _acceleration: = 15.0


func _ready() -> void:
	__test()
	$WhaleModel/AnimationPlayer.get_animation("Idle").loop = true
	$WhaleModel/AnimationPlayer.get_animation("Walk").loop = true
	$WhaleModel/AnimationPlayer.set_blend_time("Walk", "Idle", 0.5)
	$WhaleModel/AnimationPlayer.set_blend_time("Idle", "Walk", 0.5)
	$WhaleModel/AnimationPlayer.play("Idle")


func _physics_process(_delta: float) -> void:
	_velocity = move_and_slide(_velocity)


func _process(delta: float) -> void:
	_update_velocity(delta)
	_update_rotation(delta)
	_update_state()


func _change_state(state: int) -> void:
	if state == _state:
		return
	_state = state
	if state == IDLE:
		$WhaleModel/AnimationPlayer.play("Idle")
	elif state == WALKING:
		$WhaleModel/AnimationPlayer.play("Walk")


func _update_velocity(delta: float) -> void:
	var dir: = _get_walk_direction()
	
	if dir == Vector3():
		var vel_xz: = Vector2(_velocity.x, _velocity.z)
		if _acceleration * delta > vel_xz.length():
			_velocity.x = 0.0
			_velocity.z = 0.0
		else:
			_velocity.x -= vel_xz.normalized().x * _acceleration * delta
			_velocity.z -= vel_xz.normalized().y * _acceleration * delta
	else:
		_velocity += _acceleration * dir * delta
	
	var vel_xz: = Vector2(_velocity.x, _velocity.z)
	if vel_xz.length() > _max_speed:
		vel_xz = vel_xz.normalized() * _max_speed
		_velocity.x = vel_xz.x
		_velocity.z = vel_xz.y
		
	_velocity += Global.gravity * delta


func _update_rotation(delta: float) -> void:
	var dir: = Vector2(_velocity.x, _velocity.z).normalized()
	if dir == Vector2():
		return
	dir = dir.normalized()
	var forward_pos: = translation + Vector3(dir.x, 0.0, dir.y)
	var target_transform: = transform.looking_at(forward_pos, Vector3(0.0, 1.0, 0.0))
	var facing_rot: = transform.basis.get_euler().y
	var target_rot: = target_transform.basis.get_euler().y
	var diff: = target_rot - facing_rot
	if diff > PI: diff -= 2 * PI
	if diff < -PI: diff += 2 * PI
	if abs(diff) < _anglar_speed * delta:
		transform = target_transform
	else:
		transform.basis = transform.basis.rotated(
			Vector3(0.0, 1.0, 0.0), sign(diff) * _anglar_speed * delta)


func _update_state() -> void:
	if _velocity.x == 0.0 and _velocity.z == 0.0:
		_change_state(IDLE)
		$WhaleModel/AnimationPlayer.playback_speed = 1.0
	else:
		_change_state(WALKING)
		$WhaleModel/AnimationPlayer.playback_speed = (
			Vector2(_velocity.x, _velocity.z).length() / _base_speed)


func _get_walk_direction() -> Vector3:
	var dir: = Vector3()
	dir.z -= Input.get_action_strength("walk_forward")
	dir.z += Input.get_action_strength("walk_backward")
	dir.x -= Input.get_action_strength("walk_left")
	dir.x += Input.get_action_strength("walk_right")
	dir = dir.rotated(Vector3(0.0, 1.0, 0.0), _get_camera_y_rot())
	if dir.length() > 1.0:
		return dir.normalized()
	else:
		return dir


func _get_camera_y_rot() -> float:
	return (get_node(_camera_path) as Camera).transform.basis.get_euler().y


func __test() -> void:
	assert(_camera_path != null)
