extends Area2D


const RANGE_COLOR_VALID = Color(0.2, 0.8, 0.4, 0.3)
const RANGE_COLOR_INVALID = Color(0.8, 0.2, 0.4, 0.3)
const LASER_COLOR_OUTER = Color(0.2, 0.5, 0.9, 0.8)
const LASER_COLOR_INNER = Color(1.0, 1.0, 1.0, 0.9)

export var DPS = 100
export var INITIAL_RANGE = 100
export var ENERGY_CONSUMPTION = 10

var active = false
var energy_level = 0

var target_strategy
var targets = []
var current_target

const COST = 30
var current_value = COST/3

var fire_range
var range_visible = 0

var process_func
var input_func
var area_entered_func
var area_exited_func

var valid_placement = true
var c = RANGE_COLOR_VALID

func _ready():
	set_range(INITIAL_RANGE)
	target_strategy = TargetStrategy.new()
	set_state_preview()


func _process(delta):
	process_func.call_func(delta)


func _on_RangeDrawer_draw():
	if range_visible > 0:
		$RangeDrawer.draw_circle(Vector2(0,0), fire_range, c)


func _on_LaserDrawer_draw():
	if current_target != null:
		$LaserDrawer.draw_line(Vector2(-8,-30), current_target.global_position-global_position, LASER_COLOR_OUTER, 6)
		$LaserDrawer.draw_line(Vector2(-8,-30), current_target.global_position-global_position, LASER_COLOR_INNER, 2)


func show_range(state):
	if state:
		range_visible+=1
	else:
		range_visible = max(range_visible-1, 0) # decrements should never go below 0, but just to be sure
	$RangeDrawer.update()

func set_range_color(new_color):
	c = new_color
	$RangeDrawer.update()

func set_range(new_range):
	fire_range = new_range
	$TowerRange.get_shape().radius = fire_range


func set_valid_placement(state):
	valid_placement = state
	if valid_placement:
		set_range_color(RANGE_COLOR_VALID)
	else:
		set_range_color(RANGE_COLOR_INVALID)


func set_state_preview():
	$AnimatedSprite.modulate.a = 0.5
	show_range(true)
	monitoring = false
	monitorable = true
	process_func = funcref(self, "preview_process")
	area_entered_func = funcref(self, "preview_on_Tower_area_entered")
	area_exited_func = funcref(self, "preview_on_Tower_area_exited")

func preview_process(delta):
	if get_tree().is_network_server():
		position = get_global_mouse_position()
		rpc_unreliable("update_position", position)
		if Input.is_mouse_button_pressed(BUTTON_LEFT):
			rpc("set_state_built")
		elif Input.is_mouse_button_pressed(BUTTON_RIGHT) or Input.is_key_pressed(KEY_ESCAPE):
			rpc("destory")


remote func update_position(new_pos):
	position = new_pos

sync func destroy():
	queue_free()

sync func set_state_built():
	if not valid_placement:
		return
	process_func = funcref(self, "built_process")
	input_func = funcref(self, "built_input")
	area_entered_func = funcref(self, "built_on_Tower_area_entered")
	area_exited_func = funcref(self, "built_on_Tower_area_exited")
	$AnimatedSprite.modulate.a = 1
	$TowerBase.set_built()
	show_range(false)
	monitoring = true
	monitorable = false
	set_pickable(true)
	$RangeDrawer.z_index = -1
	$RangeDrawer.z_as_relative = false
	get_parent().get_parent().tower_constructed()


func built_process(delta):
	shoot(delta)


func sell():
	queue_free()
	return current_value


func add_target(t):
	targets.append(t)
	targets.sort_custom(target_strategy, "sort")

func remove_target(t):
	targets.erase(t)

func shoot(delta):
		if active and targets.size() > 0:
			current_target = targets.front()
			current_target.take_damage( DPS * delta )
			$LaserDrawer.update()
		elif current_target != null:
			current_target = null
			$LaserDrawer.update()


func _on_TowerBase_invalid_location():
	set_valid_placement(false)

func _on_TowerBase_valid_location():
	set_valid_placement(true)


func _on_Tower_area_entered( area ):
	area_entered_func.call_func( area )

func _on_Tower_area_exited( area ):
	area_exited_func.call_func( area )


func preview_on_Tower_area_entered( area ):
	show_range(true)

func preview_on_Tower_area_exited( area ):
	show_range(false)


func built_on_Tower_area_entered( area ):
	#print("Area entered", area )
	if area.has_method("enter_range"):
		area.enter_range() #Notify mob it is in range
		add_target(area) #Add mob to list of targets

func built_on_Tower_area_exited( area ):
	#print("Area exited", area)
	if area.has_method("exit_range"):
		area.exit_range() #Notify mob it has left range (or this particular tower)
		remove_target(area)


func _on_TowerBase_mouse_entered_base():
	show_range(true)

func _on_TowerBase_mouse_exited_base():
	show_range(false)


class TargetStrategy:
	var selected_sorter
	var sorters = [
		funcref(self, "progress_far"),
		funcref(self, "progress_short"),
		funcref(self, "hp_low"),
		funcref(self, "hp_high")
	]
	
	func _init():
		selected_sorter = sorters[0]
	
	func progress_short(a, b):
		return a.progress < b.progress
	
	func progress_far(a, b):
		return a.progress > b.progress
	
	func hp_low(a, b):
		return a.get_health() < b.get_health()
	
	func hp_high(a, b):
		return a.get_health() > b.get_health()
	
	func sort(a, b):
		return selected_sorter.call_func(a,b)


func _on_TowerBase_tower_activated():
	active = true

func _on_TowerBase_tower_deactivated():
	active = false
