extends KinematicBody2D

export var debugMode = false

var trajectory:String = 'line'
var trajectory_line = 'line'

var bullet
var username_text = load("res://source/scenes/OVERLAY/elements/username_text.tscn")
var username setget username_set
var username_text_instance = null
var health_bar_scene = load("res://source/scenes/OVERLAY/elements/HUD.tscn")
var health_bar_instance = null
var hp = 100 setget set_hp
var can_shoot = true
var is_reloading = false


puppet var puppet_hp = 100 setget puppet_hp_set
puppet var puppet_position = Vector2(0, 0) setget puppet_position_set
puppet var puppet_velocity = Vector2()
puppet var puppet_rotation = 0
puppet var puppet_username = "" setget puppet_username_set
puppet var puppet_weapon_position = Vector2()
puppet var puppet_weapon_angle = 0
puppet var puppet_direction = "left"
puppet var puppet_theme = "01"
puppet var puppet_character_states = {}
puppet var puppet_bullet_position setget puppet_bullet_position_set
puppet var puppet_phase setget puppet_phase_set

onready var tween = $Tween
onready var sprite = $player_sprite
onready var reload_timer = $reload_timer
onready var shoot_point = $"weaponHolder/Player-character-theme-gun/shoot_point"
onready var hit_timer = $hit_timer


var bullet_env = {
	'line' : preload("res://source/entities/shooting/Line_Trajectory/Line_Env.tscn"),
	'sine' : preload("res://source/entities/shooting/Sine_Trajectory/Sine_Env.tscn"),
	'parab' : preload("res://source/entities/shooting/Parabolic_Trajectory/Parabolic_Env.tscn"),
	'hyper' : preload("res://source/entities/shooting/Hyperbolic_Trajectory/Hyperbolic_Env.tscn")
}

var bullet_trajectory = {
	'line' : preload("res://source/entities/shooting/Line_Trajectory/Line_Barrel.tscn"),
	'sine' : preload("res://source/entities/shooting/Sine_Trajectory/Sine_Barrel.tscn"),
	'parab' : preload("res://source/entities/shooting/Parabolic_Trajectory/Parabolic_Barrel.tscn"),
	'hyper' : preload("res://source/entities/shooting/Hyperbolic_Trajectory/Hyperbolic_Barrel.tscn")
}


# Instance of data pre_processors
var VDIR_preset_pre_processor_instance = preload("res://source/assets/scripts/pre_processors/vdir_pre_processor.gd").new()
var UIN_preset_pre_processor_instance = preload("res://source/assets/scripts/pre_processors/uin_pre_processor.gd").new()

# Local class constants

# Local class variables
var VDIR = {}
var user_input = {}
var user_state = {}
var dimensions = {}

var currentMovementSpeed = 0
var maxMovementSpeed = Vector2(200,400)
var accelerationSpeed = 5
var deccelerationSpeed = 8
var rotationSpeed = 5
var currentRotation = 0
var rotationalHolder = 0
var velocityVDIR = Vector2(0,0)
var characterStates = {"onGround": false, "jumped": false, "faceDirection": true}

var reverseControls = false
var awaitingCollision = false

var direction = "left"
export var theme = "01"

var weaponRotationalStep = 2
var weaponPositionalOffset = Vector2(0,0)
var weaponPosition = Vector2(0,0)
var weaponAngle = 0

var particleTexture = ImageTexture.new()
var particleImage = Image.new()
var activeTheme = null

var globalActivePhase = null
var clientPhase = null

func _ready():
	weaponPositionalOffset = Vector2(-$"weaponHolder/Player-character-theme-gun-na3".texture.get_width() * $"weaponHolder/Player-character-theme-gun-na3".scale.x / 2,-$"weaponHolder/Player-character-theme-gun-na3".texture.get_height() * $"weaponHolder/Player-character-theme-gun-na3".scale.y / 2) + Vector2(-$weaponHolder.get_shape().get_radius(), 0)
	$"weaponHolder/Player-character-theme-gun".position = weaponPositionalOffset
# warning-ignore:return_value_discarded
	get_tree().connect("network_peer_connected", self, "_network_peer_connected")
	username_text_instance = Global.instance_node_at_location(username_text, PersistentNodes, global_position)
	username_text_instance.player_following = self
	health_bar_instance = Global.instance_node_at_location(health_bar_scene, PersistentNodes, global_position)
	health_bar_instance.player_following = self
	update_shoot_mode(false)
	Global.alive_players.append(self)

	yield(get_tree(), "idle_frame")
	if get_tree().has_network_peer():
		if is_network_master():
			Global.player_master = self
			Global.set("player", self)
	# Allow update process override.
	set_process(true)
	
	$player_animated_sprite.play("idle")
	enable_trajectory_line('line')


func get_user_state():
	# Create a dictionary of all variables that relate to clients' active state.
	user_state = {
		"global_position": global_transform.origin
	}
	return user_state


func get_dimensions():
	# Create a dictionary of all (required) sizes in regards to the client.
	dimensions = {
		"sprite": {
			"width":$player_sprite.texture.get_width(),
			"height":$player_sprite.texture.get_height(),
			"scale":$player_sprite.scale,
		},
		"collider": {
			"shape":$player_collider.get_shape(),
			"radius":$player_collider.get_shape().get_radius()
		}
	}
	return dimensions


func process_rotation():
	if rotation_degrees > 360 or rotation_degrees < -360: rotation_degrees = 0
	var VDIR_ray_positive_y = VDIR["1"]["1"]["ray"]["position"].y - user_state["global_position"].y
	var VDIR_ray_negative_y = VDIR["1"]["2"]["ray"]["position"].y - user_state["global_position"].y
	var VDIR_ray_positive_x = VDIR["1"]["1"]["ray"]["position"].x - user_state["global_position"].x
	var VDIR_ray_negative_x = VDIR["1"]["2"]["ray"]["position"].x - user_state["global_position"].x
	var VDIR_ray_offset = sqrt(VDIR_ray_positive_y * VDIR_ray_positive_y + VDIR_ray_positive_x * VDIR_ray_positive_x) - sqrt(VDIR_ray_negative_y * VDIR_ray_negative_y + VDIR_ray_negative_x * VDIR_ray_negative_x)
	if VDIR_ray_offset > 1:
		rotation_degrees += VDIR_ray_offset / 10
	if VDIR_ray_offset < 1:
		rotation_degrees += VDIR_ray_offset / 10


func _process(_delta: float) -> void:
	user_input = UIN_preset_pre_processor_instance.update(Global.get_current_phase(), self)
	if get_tree().is_network_server():
		Global.phase_update_global()
		clientPhase = Global.get_current_phase()
		theme = "03"
	else:
		if puppet_phase != null:
			clientPhase = puppet_phase
			Global.set_current_phase(clientPhase)
	$"weaponHolder/Player-character-theme-gun".play(theme)
	if theme != activeTheme:
		particleImage.load("res://source/assets/sprites/character/player/theme/"+ theme +"/na/Player-character-theme-particle-"+ theme +".png")
		particleTexture.create_from_image(particleImage)
	$Particles2D.texture = particleTexture
	if username_text_instance != null:
		username_text_instance.name = "username" + name
	if $Particles2D.position.x > 0 and direction != "left":
		$Particles2D.position = Vector2(-$Particles2D.position.x,$Particles2D.position.y)
		$Particles2D.scale = -$Particles2D.scale
	elif $Particles2D.position.x < 0 and direction != "right":
		$Particles2D.position = Vector2(-$Particles2D.position.x,$Particles2D.position.y)
		$Particles2D.scale = -$Particles2D.scale
	user_state = get_user_state()
	dimensions = get_dimensions()
	VDIR = VDIR_preset_pre_processor_instance.update(user_state, dimensions)
	for vector_type in VDIR:
		var v_t = str(vector_type)
		for vector in VDIR[v_t]:
			var v = str(vector)
			if v_t == "1":
				var space_state = get_world_2d().direct_space_state
				var ray_cast = space_state.intersect_ray((VDIR[v_t][v]["start"] - user_state["global_position"]).rotated(rotation) + user_state["global_position"],(VDIR[v_t][v]["end"] - user_state["global_position"]).rotated(rotation) + user_state["global_position"],[self])
				if "position" in ray_cast:
					VDIR[v_t][v]["ray"]["position"] = ray_cast.position
					VDIR[v_t][v]["ray"]["length"] = sqrt((VDIR[v_t][v]["ray"]["position"].y - user_state["global_position"].y)*(VDIR[v_t][v]["ray"]["position"].y - user_state["global_position"].y)+(VDIR[v_t][v]["ray"]["position"].x - user_state["global_position"].x)*(VDIR[v_t][v]["ray"]["position"].x - user_state["global_position"].x))
					VDIR[v_t][v]["ray"]["offset"] = dimensions["collider"]["radius"] - VDIR[v_t][v]["ray"]["length"]
					VDIR[v_t][v]["ray"]["collided"] = true
				else:
					VDIR[v_t][v]["ray"]["position"] = (VDIR[v_t][v]["end"] - user_state["global_position"]).rotated(rotation) + user_state["global_position"]
					VDIR[v_t][v]["ray"]["length"] = sqrt((VDIR[v_t][v]["ray"]["position"].y - user_state["global_position"].y)*(VDIR[v_t][v]["ray"]["position"].y - user_state["global_position"].y)+(VDIR[v_t][v]["ray"]["position"].x - user_state["global_position"].x)*(VDIR[v_t][v]["ray"]["position"].x - user_state["global_position"].x))
					VDIR[v_t][v]["ray"]["offset"] = dimensions["collider"]["radius"] - VDIR[v_t][v]["ray"]["length"]
					VDIR[v_t][v]["ray"]["collided"] = false
	update()
	process_rotation()


func _physics_process(delta) -> void:
	if get_tree().has_network_peer():
		if is_network_master() and visible:
			if "0" in VDIR:
				if VDIR["1"]["0"]["ray"]["length"] - dimensions["collider"]["radius"] > 5:
					characterStates["onGround"] = false
				else:
					characterStates["onGround"] = true
					characterStates["jumped"] = false
				if characterStates["onGround"] and abs(velocityVDIR.x) < 5:
					if rotation_degrees > 90 or rotation_degrees < -90:
						reverseControls = true
					else:
						reverseControls = false
				if VDIR["1"]["0"]["ray"]["collided"]:
					rotationalHolder = rotation
				if characterStates["jumped"] and not VDIR["1"]["0"]["ray"]["collided"]:
					awaitingCollision = true
				elif characterStates["jumped"] and awaitingCollision and VDIR["1"]["0"]["ray"]["collided"]:
					awaitingCollision = false
					if velocityVDIR.y < 0: velocityVDIR.y = 0
				if user_input["boost"] and not characterStates["jumped"]:
					maxMovementSpeed.x = move_toward(maxMovementSpeed.x, 350, accelerationSpeed)
				else:
					maxMovementSpeed.x = move_toward(maxMovementSpeed.x, 200, accelerationSpeed)
				if characterStates["onGround"] and not characterStates["jumped"] and user_input["up"]:
					velocityVDIR.y = -maxMovementSpeed.y
					characterStates["jumped"] = true
					rotationalHolder = rotation
				if user_input["left"] and not reverseControls or user_input["right"] and reverseControls:
					if velocityVDIR.x > 0:
						velocityVDIR.x -= deccelerationSpeed
					else:
						velocityVDIR.x -= accelerationSpeed
						if not characterStates["jumped"]:
							direction = "left"
					if velocityVDIR.y < maxMovementSpeed.x and not characterStates["onGround"] and characterStates["jumped"]:
						rotation_degrees -= rad2deg(rotationSpeed*delta)
				if user_input["right"] and not reverseControls or user_input["left"] and reverseControls:
					if velocityVDIR.x < 0:
						velocityVDIR.x += deccelerationSpeed
					else:
						velocityVDIR.x += accelerationSpeed
						if not characterStates["jumped"]:
							direction = "right"
					if velocityVDIR.y < maxMovementSpeed.x and not characterStates["onGround"] and characterStates["jumped"]:
						rotation_degrees += rad2deg(rotationSpeed*delta)
				if characterStates["jumped"] and not characterStates["onGround"] and velocityVDIR.y > maxMovementSpeed.x:
					if rotation_degrees > rad2deg(rotationalHolder): rotation_degrees -= rotationSpeed / 2
					elif rotation_degrees < rad2deg(rotationalHolder): rotation_degrees = move_toward(rotation_degrees, rad2deg(rotationalHolder), rotationSpeed / 2)
				if not user_input["right"] and not user_input["left"]:
					velocityVDIR.x = move_toward(velocityVDIR.x, 0, deccelerationSpeed)
				if velocityVDIR.x != 0 and maxMovementSpeed.x == 200:
					$player_animated_sprite.play("move-speed-" + direction + "-" + theme)
				elif maxMovementSpeed.x > 200 and not characterStates["jumped"]:
					$player_animated_sprite.play("boost-speed-" + direction + "-" + theme)
					$Particles2D.set_emitting(true)
				else:
					#$player_animated_sprite.play("idle-speed-"+direction+"-"+theme)
					$Particles2D.set_emitting(false)
				if not characterStates["onGround"]:
					velocityVDIR.y += accelerationSpeed
				elif characterStates["onGround"] and velocityVDIR.y > 0:
					velocityVDIR.y -= deccelerationSpeed
				velocityVDIR = Vector2(clamp(velocityVDIR.x, -maxMovementSpeed.x, maxMovementSpeed.x), clamp(velocityVDIR.y, -maxMovementSpeed.y, maxMovementSpeed.y))
# warning-ignore:return_value_discarded
				move_and_slide(velocityVDIR.rotated(rotationalHolder))
				rotate_weapon()
				if user_input["shoot"] and can_shoot and not is_reloading:
					rpc("shoot", trajectory)
					is_reloading = true
					reload_timer.start()
		else:
			rotation = lerp_angle(rotation, puppet_rotation, delta * 8)
			$"weaponHolder/Player-character-theme-gun".position = puppet_weapon_position
			weaponAngle = puppet_weapon_angle
			direction = puppet_direction

			if velocityVDIR.x != 0 and maxMovementSpeed.x == 200:
				$player_animated_sprite.play("move-speed-" + direction + "-" + theme)
			elif maxMovementSpeed.x > 200 and not characterStates["jumped"]:
				$player_animated_sprite.play("boost-speed-" + direction + "-" + theme)
				$Particles2D.set_emitting(true)
			else:
				$player_animated_sprite.play("idle-speed-" + direction + "-" + theme)
				$Particles2D.set_emitting(false)
			rotate_weapon()

			if not tween.is_active():
				pass
	if hp <= 0:
		if get_tree().is_network_server():
			rpc("destroy")
			Global.get("killed_players").append(self)



sync func shoot(new_trajectory:String):
	bullet = bullet_env[new_trajectory].instance()
	get_parent().add_child(bullet)
	bullet.global_position = shoot_point.global_position
	bullet.global_rotation = shoot_point.global_rotation


func enable_trajectory_line(new_trajectory_line:String):
	for x in get_node('weaponHolder/Player-character-theme-gun/shoot_point').get_children():	#if there is gun remove it
		x.queue_free()
		
	var x = bullet_trajectory[new_trajectory_line].instance()
#	print(x)
	get_node('weaponHolder/Player-character-theme-gun/shoot_point').add_child(x)
	


func _draw():
	if debugMode:
		for vector_type in VDIR:
			var v_t = str(vector_type)
			for vector in VDIR[v_t]:
				var v = str(vector)
				if v_t == "1":
					draw_line(VDIR[v_t][v]["start"] - user_state["global_position"],(VDIR[v_t][v]["ray"]["position"] - user_state["global_position"]).rotated(-rotation),Color(255,255,255,1),1)


func puppet_phase_set(new_value) -> void:
	puppet_phase = new_value


func puppet_position_set(new_value) -> void:
	puppet_position = new_value
	tween.interpolate_property(self, "global_position", global_position, puppet_position, 0.1)
	tween.start()


func puppet_bullet_position_set(new_value) -> void:
	puppet_bullet_position = new_value
	tween.interpolate_property(self, "global_position", global_position, puppet_bullet_position, 0.1)
	tween.start()


func set_hp(new_value):
	hp = new_value
	if get_tree().has_network_peer():
		if is_network_master():
			rset("puppet_hp", hp)


func puppet_hp_set(new_value):
	puppet_hp = new_value
	if get_tree().has_network_peer():
		if not is_network_master():
			hp = puppet_hp


func username_set(new_value) -> void:
	username = new_value
	if is_network_master() and username_text_instance != null:
		username_text_instance.text = username
		rset("puppet_username", username)


func puppet_username_set(new_value) -> void:
	puppet_username = new_value
	if not is_network_master() and username_text_instance != null:
		username_text_instance.text = puppet_username


func _network_peer_connected(id) -> void:
	rset_id(id, "puppet_username", username)


func _on_network_tick_rate_timeout():
	if get_tree().has_network_peer():
		if is_network_master():
			rset_unreliable("puppet_position", global_position)
			rset_unreliable("puppet_rotation", rotation)
			rset_unreliable("puppet_weapon_position", weaponPosition)
			rset_unreliable("puppet_weapon_angle", weaponAngle)
			rset_unreliable("puppet_direction", direction)
			#rset_unreliable("puppet_character_states", characterStates)
			rset_unreliable("puppet_bullet_position", bullet)
			if get_tree().is_network_server():
				rset_unreliable("puppet_phase", clientPhase)


sync func update_position(pos):
	global_position = pos
	puppet_position = pos


func update_shoot_mode(shoot_mode):
	can_shoot = shoot_mode


func _on_reload_timer_timeout():
	is_reloading = false


func _on_hit_timer_timeout():
	modulate = Color(1, 1, 1, 1)


func _on_hitbox_area_entered(area):
	if get_tree().is_network_server():
		if area.is_in_group("Player_damager"):
			rpc("hit_by_damager", area.get_parent().damage)
			area.get_parent().rpc("destroy")


sync func hit_by_damager(damage):
	hp -= damage
	modulate = Color(5, 5, 5, 1)
	health_bar_instance.value = hp
	hit_timer.start()


sync func enable() -> void:
	hp = 100
	health_bar_instance.value = 100
	can_shoot = false
	update_shoot_mode(false)
	username_text_instance.visible = true
	health_bar_instance.visible = true
	visible = true
	$player_collider.disabled = false
	$hitbox/CollisionShape2D.disabled = false
	$weaponHolder.disabled = false
	if get_tree().has_network_peer():
		if is_network_master():
			Global.player_master = self
	if not Global.alive_players.has(self):
		Global.alive_players.append(self)
	weaponPositionalOffset = Vector2(-$"weaponHolder/Player-character-theme-gun-na3".texture.get_width() * $"weaponHolder/Player-character-theme-gun-na3".scale.x / 2,-$"weaponHolder/Player-character-theme-gun-na3".texture.get_height() * $"weaponHolder/Player-character-theme-gun-na3".scale.y / 2) + Vector2(-$weaponHolder.get_shape().get_radius(), 0)
	$"weaponHolder/Player-character-theme-gun".position = weaponPositionalOffset


sync func destroy() -> void:
	username_text_instance.visible = false
	health_bar_instance.visible = false
	visible = false
	$player_collider.disabled = true
	$hitbox/CollisionShape2D.disabled = true
	$weaponHolder.disabled = true
	Global.alive_players.erase(self)
	if get_tree().has_network_peer():
		if is_network_master():
			Global.player_master = null


func _exit_tree() -> void:
	Global.alive_players.erase(self)
	if get_tree().has_network_peer():
		if is_network_master():
			Global.player_master = null


func rotate_weapon():
	weaponPosition = $"weaponHolder/Player-character-theme-gun".position
	weaponPosition -= Vector2(weaponPositionalOffset.x,0).rotated(deg2rad(weaponAngle)) + Vector2(0,weaponPositionalOffset.y)
	if user_input["r_inc"]:
		weaponAngle += weaponRotationalStep
	if user_input["r_dec"]:
		weaponAngle -= weaponRotationalStep
	if direction == "right":
		if weaponAngle + weaponRotationalStep < 87.5:
			weaponAngle = 180 - weaponAngle
		weaponAngle = clamp(weaponAngle, 87.5,180)
		$"weaponHolder/Player-character-theme-gun".flip_v = true
	elif direction == "left":
		if weaponAngle - weaponRotationalStep > 92.5:
			weaponAngle = abs(weaponAngle - 180)
		weaponAngle = clamp(weaponAngle, 0, 92.5)
		$"weaponHolder/Player-character-theme-gun".flip_v = false
	weaponPosition += Vector2(weaponPositionalOffset.x,0).rotated(deg2rad(weaponAngle)) + Vector2(0,weaponPositionalOffset.y)
	$"weaponHolder/Player-character-theme-gun".position = weaponPosition
	$"weaponHolder/Player-character-theme-gun".rotation_degrees = weaponAngle
	pass
