module(..., package.seeall)

k_focus_mode_none= 0
k_focus_mode_normal= 1
k_focus_mode_wide= 2

k_scale_out= 10
k_scale_fade_start= 8.5
k_scale_normal= 1
k_scale_wide= 2.5

k_focus_transition_time= 1
k_focus_alpha= 0.75

k_tapme_message_delay= 3

k_states= {}

k_left= e_value("e_text_horizontal_alignment", "_text_horizontal_alignment_left")
k_center= e_value("e_text_horizontal_alignment", "_text_horizontal_alignment_center")
k_right= e_value("e_text_horizontal_alignment", "_text_horizontal_alignment_right")

k_escape_key= e_value("e_key", "_key_escape")
k_enter_key= e_value("e_key", "_key_enter")


function update_focus_transition(focus, time, start_time, end_time, start_scale, end_scale)

	local scale= 1
	local finished= false

	if (time > end_time) then
		scale= end_scale
		finished= true
	else
		local delta_y= end_scale - start_scale		
		local t= (time - start_time)/(end_time - start_time)
		scale= start_scale + 0.5*delta_y*(1 - math.cos(t*math.pi))
	end

	focus.scale_set_relative(scale)
	
	local tint= focus.tint_get()
	if (tint) then
		local alpha= k_focus_alpha
		if (scale > k_scale_fade_start) then
			alpha= k_focus_alpha * (1 - (scale - k_scale_fade_start)/(k_scale_out - k_scale_fade_start))
		end

		tint.tint.alpha= alpha
	end

	return finished
end

function internal_make_state(keep_focus)
	local state= {}
	state.placement_name= nil
	state.delay= 0
	state.pause= false
	state.focus_on= false
	state.focus_scale= 0
	state.focus_object_name= nil
	state.keep_focus= keep_focus
	state.start_func= nil
	state.end_func= nil
	
	if (keep_focus) then
		-- copy from the previous state
		local previous_state= k_states[#k_states]
		if (previous_state) then
			state.focus_on= previous_state.focus_on
			state.focus_scale= previous_state.focus_scale
		end
	end
	
	table.insert(k_states, state)
	
	return state
end

function make_state(placement_name, keep_focus)
	local state= internal_make_state(keep_focus)
	state.placement_name= placement_name
	state.pause= true
	
	return state
end

function add_delay(delay, keep_focus)
	local state= internal_make_state(keep_focus)
	state.delay= delay
	
	return state
end

function focus_on(state, object_name, scale)
	state.focus_on= true
	state.focus_scale= scale
	state.focus_object_name= object_name
end


function start()

	local campaign= campaign_get()
	if (campaign.reset_count_get() + campaign.fail_count_get() >= 2) then
		sleep()
	else
		set_state(1)
	end
end

function set_state(state_index)
	local ws= world_state_get()
	local camera= object_get_from_plate("hud", "camera_control")
	local camera_control= nil
	if (camera) then
		camera_control= camera._c.camera_control
	end
	
	global.state= state_index
	local state= k_states[global.state]
	if (state) then
		global.timer= state.delay
	else
		global.timer= 0
	end

	if (state and state.pause) then
		ws.game_time_pause()
	else
		ws.game_time_play()
	end
	
	if (state and state.keep_focus) then
		-- don't change anything about the focus
	else
		if (state and state.focus_on) then
			if (global.focus.is_hidden()) then
				place_focus()
			end
		end

		if (global.focus) then
			global.focus_start_time= ws.master_time.time
			global.focus_start_scale = global.focus.scale_relative_get()
			
			if (state and state.focus_on and state.focus_object_name) then
				local focus_object= object_get(state.focus_object_name)
				if (focus_object) then
					local pos= focus_object.world_position_get()
					global.focus.position_set(pos.x, pos.y)
					
					if (camera_control and not focus_object.plate.screen) then
						local p= ws.camera[0].world_to_world(pos.x, pos.y, focus_object.depth_get(), 0)
						camera_control.scripted_focus_point.set(p.x, p.y)
						camera_control.scripted_focus_valid= true
					end
				end
			else

				-- turning focus off
				if (camera_control) then
					camera_control.scripted_focus_valid= false
				end

			end
		end
	end
	
	global.message= nil
	local message_valid= false
	if (state and state.placement_name) then
		global.message= object_place(state.placement_name)
		if (global.message) then
			if (global.message.plate.screen) then
				global.message.tween_animation_play("swim_x_screen")
				global.message.tween_animation_play("swim_y_screen")
			else
				global.message.tween_animation_play("swim_x")
				global.message.tween_animation_play("swim_y")
			end
			
			if (not state.pause) then
				local input= global.message.input_get()
				if (input) then
					input.disable()
				end
			end
			
			-- update (possibly) the scripted camera bounds, so we can be sure we will see the messages
			if (camera_control and not global.message.plate.screen) then
				local msg_bounds= global.message.bounding_box
				local top_left= {}
				local bottom_right= {}
				local msg_depth= global.message.depth_get()
				local camera= ws.camera[0]
				top_left.x= msg_bounds.minx
				top_left.y= msg_bounds.maxy
				bottom_right.x= msg_bounds.maxx
				bottom_right.y= msg_bounds.miny
				
				local alignment= k_left
				local msg_text= global.message.text_get()
				if (msg_text) then
					alignment= msg_text.get_horizontal_alignment()
				end
				if (alignment==k_left) then
					bottom_right.x= bottom_right.x+8
				elseif (alignment==k_center) then
					top_left.x= top_left.x-4
					bottom_right.x= bottom_right.x+4
				elseif (alignment==k_right) then
					top_left.x= top_left.x-8
				end					
				
				
				top_left= camera.world_to_world(top_left.x, top_left.y, msg_depth, 0)
				bottom_right= camera.world_to_world(bottom_right.x, bottom_right.y, msg_depth, 0)
				
				local scripted_bounds= camera_control.scripted_bounds
				
				if (camera_control.scripted_bounds_valid) then
					-- aggregate
					scripted_bounds.minx= math.min(top_left.x, scripted_bounds.minx)
					scripted_bounds.maxx= math.max(bottom_right.x, scripted_bounds.maxx)
					scripted_bounds.miny= math.min(bottom_right.y, scripted_bounds.miny)
					scripted_bounds.maxy= math.max(top_left.y, scripted_bounds.maxy)
				else
					-- replace
					scripted_bounds.minx= top_left.x
					scripted_bounds.maxx= bottom_right.x
					scripted_bounds.miny= bottom_right.y
					scripted_bounds.maxy= top_left.y
				end
				
				camera_control.scripted_bounds_valid= true
				
			end
						
			message_valid= true
		end
	end		
	if (not message_valid and camera_control) then
		-- clear the scripted bounds
		camera_control.scripted_bounds_valid= false
	end
	
	if (state and state.start_func) then
		state.start_func()
	end

end

function place_focus()
	local ws= world_state_get()
	if (global.focus) then
		global.focus.hide(false)
		global.focus.scale_set_relative(k_scale_out)
	end
end

function update_focus(state)
	if (global.focus and global.focus_start_time ~= -1) then
		local ws= world_state_get()
		local target_scale= k_scale_out
		if (state and state.focus_on) then
			target_scale= state.focus_scale
		end

		local end_time= global.focus_start_time + k_focus_transition_time
		local finished= update_focus_transition(global.focus, ws.master_time.time, global.focus_start_time, end_time, global.focus_start_scale, target_scale)
		if (finished) then
		
			if (not (state and state.focus_on)) then
				global.focus.hide(true)
			end

			global.focus_start_time= -1
		end
	end
end


-- eugh total hack
function clear_player_touches()
	local ws= world_state_get()
	local iterator= ws.component_cache.component_player.iterator_get()
	while (true) do
		local player= iterator.next()
		if (player) then
			local player_control= player.object._c.player_control
			if (player_control) then
				player_control.reset_touch= true
			end
		else
			break
		end
	end
end


function update()

	local ws= world_state_get()
	local master_time= ws.master_time.time
	local delta_t= ws.time.delta_t()
	local state_changed= false
	local advance= false

	local current_state= k_states[global.state]
	
	if (current_state) then
		if (current_state.pause and global.message) then
			local touch_interface= touch_get()
			if (touch_interface and touch_interface.touch_started()) then
				advance= true
				clear_player_touches()
			else
				--[[
				local keyboard= keyboard_get()
				if (keyboard) then
					if (keyboard.pressed[k_escape_key] or keyboard.pressed[k_enter_key]) then
						advance= true
					end
				end
				--]]
			end
		else
			if (global.timer > 0) then
				global.timer = global.timer - delta_t
			end

			if (global.timer <= 0) then
				if (current_state.end_func) then
					advance= current_state.end_func()
				else
					advance= true
				end
			end
		end
	end

	-- advance
	if (advance) then
		if (global.message) then
			local tween= global.message.tween_get()
			tween.stop_all()
			
			-- restore my original location!
			global.message.restore_initial_transform()			
			tween.play_animation("lost_focus")
			
			global.message.delete_all_children()
			
			local input= global.message.input_get()
			if (input) then
				input.disable()
			end

		end
		set_state(global.state+1)
		state_changed= true
	else
		-- after some time, place the "tapme" notice under the message
		--[[
		if (global.message and not global.message.attachment.first_child) then
			local dt= master_time - global.message.placement_time
			if (dt > k_tapme_message_delay) then
				local tapme= object_place("tapme")
				if (tapme) then
					local msg_scale= global.message.transform.scale

					ws.object_move_to_plate(tapme, global.message.plate);
					
					-- we need to fix the scale on this object after moving it to the new plate
					if (global.message.plate.screen) then
						-- no need to scale, since we're moving to another screen plate
					else
						local camera= ws.camera[0]
						local y_bounds= camera.y_bounds(global.message.depth_get())
						local ratio= y_bounds/1.0		-- 2.0 is the bounds of the screen space plate we transferred from
						local scale= tapme.transform.scale
						tapme.scale_set(scale.x*ratio/msg_scale.x, scale.y*ratio/msg_scale.y)
					end
					
					object_attach(global.message, nil, tapme, nil)
					local local_position= tapme.transform.offset
					local box= global.message.bounding_box
					local box_height= box.maxy-box.miny
					local x= (box.maxx+box.minx)/2.0
					
					x= (x-global.message.transform.offset.x)/msg_scale.x
					
					--print("local position y: "..local_position.y)
					--print("box height: "..box_height)
					--print("msg_scale: "..msg_scale.y)
					--print("-box_height/(2.0*msg_scale.y): "..-box_height/(2.0*msg_scale.y))
					
					--tapme.position_set(local_position.x, -box_height/(2.0*msg_scale.y))
					tapme.position_set(x, -box_height/(2.0*msg_scale.y))
				end
			end
		end
		--]]
	end

	update_focus(current_state)

	return state_changed
end


function remove_bubble(name)
	local bubble= object_get(name)
	if (bubble) then
		local tween= bubble.tween_get()
		if (tween) then
			tween.stop_all()
			tween.play_animation("end")
		else
			object_delete(bubble)
		end
	end
end
