:NAME player_control

:CONFIG boolean		skip_win_screen
:CONFIG boolean		hide_paths

:PARAM s_ping_effect@	sighted_ping
:PARAM s_ping_effect@	lost_sight_ping
:PARAM s_ping_effect@	invalid_gear_ping
:PARAM boolean			can_pick_up_loot
:PARAM boolean			causes_general_alert
:PARAM boolean			exit_is_optional

:VAR uint32			active_touch_index
:VAR boolean		touch_active
:VAR boolean		endpoint_active
:VAR boolean		target_active
:VAR boolean		no_more_steps
:VAR boolean		is_immediate
:VAR boolean		exited
:VAR boolean		disabled
:VAR boolean		invalid_gear_ping_pending
:VAR boolean		first_move
:VAR boolean		reset_touch
:VAR c_object*		rendered_line
:VAR c_object*		endpoint
:VAR c_object*		target

:GLOBAL boolean		played_start_move_sound

-- ---------------------- constants ----------------------

k_game_time_mode= e_value("e_time_mode", "_time_mode_game")

k_time_to_get_to_desired_radial_distance_default= 1.5
k_time_to_get_to_desired_radial_distance_defense= 0.5

k_a=		e_value("e_key", "_key_a")
k_s=		e_value("e_key", "_key_s")
k_d=		e_value("e_key", "_key_d")
k_w=		e_value("e_key", "_key_w")

k_acceleration= 500.0

k_primary_player= e_value("e_player", "_player_primary")

local vectorlib = require "scripts/vector_math"

-- ---------------------- code ----------------------

function config_initialize(c)
	c.skip_win_screen= false
	c.hide_paths= false
end

function param_initialize(p)
	p.sighted_ping= nil
	p.lost_sight_ping= nil
	p.invalid_gear_ping= nil
	p.can_pick_up_loot= false
	p.exit_is_optional= false
end

function var_initialize(v)
	-- do nothing
	v.touch_active= false
	v.active_touch_index= -1
	v.rendered_line= nil
	v.endpoint= nil
	v.target= nil
	
	v.endpoint_active= false
	v.target_active= false
	v.no_more_steps= false
	v.is_immediate= false
	v.exited= false
	v.disabled= false
	
	v.invalid_gear_ping_pending= false
	v.first_move= false
	v.reset_touch= false
	
end

function global_initialize(g)
	g.played_start_move_sound= false
end

-- ---------------------- code ----------------------

function attach(object)
	local line_spawner= object.spawn_get("path_line")
	if (line_spawner) then
		var.rendered_line= line_spawner.spawn()
		if (var.rendered_line) then
			var.rendered_line.transform.identity()
		end	
	end
	
	local target_spawner= object.spawn_get("touch_target")
	if (target_spawner) then
		var.target= target_spawner.spawn()
		if (var.target) then
			local target_position= var.target.transform.offset
			local world_point= object.world_position_get()
			target_position.set(world_point.x, world_point.y)
		end	
	end
	
	local enpoint_spawner= object.spawn_get("touch_endpoint")
	if (enpoint_spawner) then
		var.endpoint= enpoint_spawner.spawn()
		if (var.endpoint) then
			var.endpoint.transform.identity()
			var.endpoint.hide(true)
		end	
	end
end


function closest_point_in_cell_to_point(arena, x, y, point)
	local result= {}
	result.x= point.x
	result.y= point.y
	
	local bb= arena.cell_get_bounding_box(x, y)
	
	if (result.x < bb.minx) then
		result.x= bb.minx
	elseif (result.x > bb.maxx) then
		result.x= bb.maxx
	end
	
	if (result.y < bb.miny) then
		result.y= bb.miny
	elseif (result.y > bb.maxy) then
		result.y= bb.maxy
	end

	return result
end


function find_neighbor_nearest_to_target(arena_state, path_radius, max_distance, cell, target_point)
	local nearest_point= nil
	local nearest_cell= nil
	local best_distance= max_distance
	local best_destination= nil
	local intermediate_destination= nil
	
	--print("Cell: "..cell.x.."    "..cell.y)
	
	for y=cell.y-1, cell.y+1 do
		for x=cell.x-1, cell.x+1 do
		
			if (x>=0 and y>=0 and arena_state.valid_coords(x, y)) then
			
				if (arena_state.cell_traversable(x, y, path_radius)) then
				
					local consider= false
					local intermediate_cell= nil
					
					--print("traversable: "..x.."    "..y)
					
					if (x==cell.x or y==cell.y) then
						consider= true
					else
						local b0= arena_state.cell_traversable(x, cell.y, path_radius)
						local b1= arena_state.cell_traversable(cell.x, y, path_radius)
						consider= b0 or b1
						
						-- in either of these cases, we need to lay down an intermediate point as well to round the corner
						if (b0 and not b1) then
							intermediate_cell={}
							intermediate_cell.x= x
							intermediate_cell.y= cell.y
						elseif (b1 and not b0) then
							intermediate_cell={}
							intermediate_cell.x= cell.x
							intermediate_cell.y= y
						end
					end
					
					if (consider) then
						local p= closest_point_in_cell_to_point(arena_state.runtime_arena, x, y, target_point)
						local distance= vectorlib.distance2d(p, target_point)
						if (distance < best_distance) then

							best_distance= distance
							best_destination= {}
							best_destination.point= p
							best_destination.cell= {}
							best_destination.cell.x= x
							best_destination.cell.y= y
							
							if (intermediate_cell) then
								intermediate_destination= {}
								intermediate_destination.cell= intermediate_cell
								intermediate_destination.point= closest_point_in_cell_to_point(arena_state.runtime_arena, intermediate_cell.x, intermediate_cell.y, target_point)
							else
								intermediate_destination= nil
							end
						end
					end
				end
			end
		end
	end
	
	return best_destination, intermediate_destination
end


-- --------------------- target / endpoint code ---------------------

function target_place()
	if (var.target) then
		local tween= var.target.tween_get()
		if (tween) then
			tween.stop_all()
			tween.play_animation("spin")
			tween.play_animation("start")
		end
		var.target.hide(false)
		var.target_active= true
	end
end

function target_remove()
	if (var.target) then
		local tween= var.target.tween_get()
		if (tween) then
			tween.stop_all()
			tween.play_animation("end")
		end
		var.target_active= false
	end
end


function endpoint_place()
	if (var.endpoint) then
		local tween= var.endpoint.tween_get()
		if (tween) then
			tween.stop_all()
			tween.play_animation("start")
		end
		var.endpoint_active= true
		local endpoint_input= var.endpoint.input_get()
		if (endpoint_input) then
			endpoint_input.enable()
		end
		var.endpoint.hide(false)
	end
end

function endpoint_remove()
	if (var.endpoint) then
		local tween= var.endpoint.tween_get()
		if (tween) then
			tween.stop_all()
			tween.play_animation("end")
		end
		local endpoint_input= var.endpoint.input_get()
		if (endpoint_input) then
			endpoint_input.disable()
		end
		var.endpoint_active= false
	end
end



-- --------------------- path management ---------------------

function path_clear(object, path)
	path.clear_path()
	
	local ws= world_state_get()
	local iterator= ws.component_cache.component_query.iterator_get()
	while (true) do
		local query= iterator.next()
		if (query) then
			local query_object= query.object
			local tint= query_object.tint_get()
			if (tint) then
				--tint.brightness= 1
				--tint.contrast= 1
				tint.tint.red= 1
				tint.tint.green= 1
				tint.tint.blue= 1
			end
		else
			break
		end
	end
end

function distance_since_last_fixed(path)

	local index= path.step_count-1
	local result= 10000
	local total_distance= 0
	local last_point= nil

	while (index>= 0) do
		local current_step= path.steps[index]
		local current_point= current_step.location.point

		if (last_point) then
			local d= vectorlib.distance2d(current_point, last_point)
			total_distance = total_distance + d
		end
		
		if (current_step.is_fixed()) then
			result= total_distance
			break;
		end
		
		last_point= current_point
		index= index-1
	end
	
	return result
end

function steps_since_last_fixed(path)

	local index= path.step_count-1
	local result= 10000
	local count= 0
	local last_point= nil

	while (index>= 0) do
		local current_step= path.steps[index]
		
		if (current_step.is_fixed()) then
			result= count
			break;
		end
		
		result= result+1		
		index= index-1
	end
	
	return result
end


function path_update(object, path)
	local touch_interface= touch_get()
	local touch= touch_interface.touch[var.active_touch_index]
	local screen_position= touch.screen_position
	local arena= world_get().arena
	local arena_state= arena.arena_state_get()
	local camera= world_state_get().camera[0]
	local new_point= camera.screen_to_world(screen_position.x, screen_position.y, 0)
	local add_to_path= true
	local waypoint= false
	local query_object_is_exit= false
	
	-- we're going to do a greedy search from our last position to get 
	-- as close as possible to the new point
	
	if (not var.target_active) then
		target_place()
	end
	if (var.target) then
		var.target.position_set(new_point.x, new_point.y)
	end
	
	local ws= world_state_get()
	local query_object= nil
	if (param.can_pick_up_loot) then
		query_object= ws.query_object_radial(new_point.x, new_point.y, 1)
	end
	if (query_object) then
	
		local query= query_object.query_get()
		local query_point= nil

		if (query.marker_handle) then
			query_point= query.marker_handle.world_position_get(query_object);
		else
			query_point= query_object.world_position_get()
		end
		
		if (query_point) then
			new_point= query_point
			waypoint= true
		end

		if (query.definition.marker_name=="exit") then
			query_object_is_exit= true
		end		
	end
	
	if (path.path_active()) then
		local inner_path= path.path
		local last_step= inner_path.steps[inner_path.step_count-1].location.point
		if (vectorlib.distance2d(last_step, new_point) < 0.01) then
			add_to_path= false
		end
	end
	
	if (add_to_path) then
		if (path.draw_path(new_point.x, new_point.y)) then
			if (waypoint) then
				local inner_path= path.path
				local last_step= inner_path.steps[inner_path.step_count-1]
				last_step.set_fixed()
				
				if (query_object_is_exit) then
					local game_hud= world_state_get().global_script("game_hud")
					if ((not game_hud) or (game_hud.required_loot_counter == 0)) then
						var.no_more_steps= true
					end
				end

				if (query_object) then
					local tint= query_object.tint_get()
					if (tint) then
						--tint.brightness= 1.5
						--tint.contrast= 1.5
						tint.tint.red= 2
						tint.tint.green= 2
						tint.tint.blue= 2
						
					end
				end
			
			end
		end
	end
end

function path_line_hide()
	var.rendered_line.hide(true)
	if (var.endpoint_active) then
		endpoint_remove()
	end
end

function path_line_show(path)
	if (not (config and config.hide_paths)) then
		var.rendered_line.hide(false)
		if (not var.endpoint_active) then
			endpoint_place()
		end
		if (var.endpoint) then
			local last_point= path.steps[path.step_count-1].location.point
			var.endpoint.position_set(last_point.x, last_point.y)
		end
	end
end


function update_render_line(object, path)
	local line= var.rendered_line
	local inner_path= path.path
	local step_count= inner_path.step_count
	if (line and path and path.path_active() and not var.disabled) then
		local count= 1 + step_count - path.step_index
		
		if (count==0) then
			path_line_hide()
		else
			
			local line_component= line.line_get()
			if (line_component) then
				local line_data= line_component.data
				local points= line_data.control_points
				local allow= false
				
				if (points.count ~= count) then
					allow= points.resize(count)
				else
					allow= true
				end

				if (allow) then
				
					path_line_show(inner_path)
					
					-- first add the object's position
					local object_point= object.world_position_get()
					points[0].point.set(object_point.x, object_point.y)
					
					local point_index= 1
					for i= path.step_index, step_count-1 do
						local step_point= inner_path.steps[i].location.point
						points[point_index].point.set(step_point.x, step_point.y)
						point_index = point_index+1
					end
					
					line_data.rebuild_bounding_box(0.5)
					line.rebuild_bounding_box()
				else
					path_line_hide()
				end
			end	
		end
	elseif (line) then
		path_line_hide()
	end
end


function update(object)

	local path= object.path_get()
	local vitality= object._c.vitality
	
	--testing line
	--local path_following= object.path_follow_get()
	--path_following.freeze_time= 1

	if (vitality and vitality.dead) then
		if (path) then
			path_clear(object, path)
		end
		return
	end

	if (path) then
		local touch_interface= touch_get()
		if (touch_interface) then
		
			if (var.touch_active) then
				local touch= touch_interface.touch[var.active_touch_index]
				if (touch.ended() or var.disabled or var.reset_touch) then
					var.touch_active= false
					var.no_more_steps= false
					var.active_touch_index= -1
					
					-- remove the target object
					if (var.target_active) then
						target_remove()
					end
				end
			elseif (var.disabled) then
				-- do nothing
			else
				local input= object.input_get()
				local started= false
				if (input) then
					local new_touch_index= input.get_touch_started()
					if (new_touch_index ~= -1) then
					
						-- touched the player
						-- start a new path
						var.active_touch_index= new_touch_index
						var.touch_active= true
						path_clear(object, path)
						endpoint_remove()
						started= true
						
						if (not global.played_start_move_sound) then
							local sound= object.sound_get()
							if (sound) then
								local start_moving_sound= sound.get_sound_by_name("start_moving")
								if (start_moving_sound) then
									start_moving_sound.play(k_game_time_mode)
									global.played_start_move_sound= true
								end
							end
						end
					end
				end
				
				if (not started and var.endpoint_active and var.endpoint) then
					local endpoint_input= var.endpoint.input_get()
					if (endpoint_input) then
						local new_touch_index= endpoint_input.get_touch_started()
						if (new_touch_index ~= -1) then
							var.active_touch_index= new_touch_index
							var.touch_active= true
							
							-- touched the endpoint
							-- continue a path
							started= true
						end
					end
				end
				
				if (started) then
					local game_hud= world_state_get().global_script("game_hud")
					if (game_hud) then
						game_hud.player_path_start_count = game_hud.player_path_start_count+1
						
						if (not var.first_move) then
							if (game_hud.first_move_time < 0.0) then
								game_hud.first_move_time= world_state_get().time.time
								game_hud.music_sneaking_play= true
							end
							var.first_move= true
						end
					end
				end
			end
			
			if (var.touch_active and not var.no_more_steps) then

				path_update(object, path)
			
			end
		end
		
		-- --------------- debug draw path ---------------
		--[[
		if (path.path_active()) then
			local inner_path= path.path
			if (inner_path.step_count > 0) then
				local last_position= object.world_position_get()
				
				local color= {}
				color.red= 0.5
				color.green= 0.5
				color.blue= 1
				
				for i=path.step_index,inner_path.step_count-1 do
					local step= inner_path.steps[i]
					local new_point= step.location.point
					
					render_line(object.plate, last_position, new_point, color)
					
					--if (step.is_fixed()) then
					--	render_circle(object.plate, new_point, 0.2, color, true)
					--else
					--	render_circle(object.plate, new_point, 0.1, color, true)
					--end

					last_position.x= new_point.x
					last_position.y= new_point.y
				end
			end
		end
		--]]
	end
	
	update_render_line(object, path)
	
	local target_component= object.target_component_get()
	local immediate= false
	if (target_component) then
		local target= target_component.target_handle
		if (target) then
			immediate= target.is_immediate()
			if (immediate and (not var.is_immediate)) then
			
				if (param.sighted_ping) then
					if (target.is_distance_map_dirty()) then
						immediate= false
					else
						target.distance_map.ping(param.sighted_ping)
					end
				end

				if (immediate) then
					local player= object.player_get()

					-- only if I'm the primary player
					if (player and player.player == k_primary_player) then
						local game_hud= world_state_get().global_script("game_hud")
						if (game_hud) then
							game_hud.player_sighted_count = game_hud.player_sighted_count+1
						end
					end
				end

			elseif ((not immediate) and var.is_immediate and param.lost_sight_ping) then
				if (target.is_distance_map_dirty()) then
					immediate= true
				else
					target.distance_map.ping(param.lost_sight_ping)
				end
			end
			
			-- might need to play an "invalid gear" ping
			if (var.invalid_gear_ping_pending) then
				if (param.invalid_gear_ping) then
					if (not target.is_distance_map_dirty()) then
						target.distance_map.ping(param.invalid_gear_ping)
						var.invalid_gear_ping_pending= false
					end
				end
			end
			
		end
	end
	
	var.is_immediate= immediate
	var.reset_touch= false
end

component_register(attach, update, nil, nil)
