note
	description: "Controller of the game library."
	author: "Louis Marchand"
	date: "Sat, 28 Mar 2015 03:42:16 +0000"
	revision: "2.1"

class 
	GAME_LIBRARY_CONTROLLER

create 
	default_create,
	make_no_parachute

feature {NONE} -- Initialization

	default_create
			-- Initialization for Current.
			-- Clean up library on segfault
		do
			initialise_library (0)
		end

	initialise_library (a_flags: NATURAL_32)
			-- Initialise the library.
		local
			l_error: INTEGER_32
		do
			is_gl_enabled := False;
			Instance_count.put (Instance_count.item + 1)
			has_error := False
			set_iteration_per_second (60)
			create internal_joysticks.make (0)
			create internal_haptics.make (0)
			l_error := {GAME_SDL_EXTERNAL}.sdl_init (a_flags)
			if l_error < 0 then
				has_error := True;
				Io.Error.put_string ("Cannot initialise the game library.%N")
			end
			check
					l_error = 0
			end
			create {LINKED_LIST [GAME_WINDOW]} internal_windows.make
			create events_controller
			make_events;
			events_controller.set_game_library (Current)
		end

	make_no_parachute
			-- Initialization for Current.
			-- Don't clean up library on segfault
		do
			initialise_library ({GAME_SDL_EXTERNAL}.sdl_init_noparachute)
		end
	
feature -- Access

	append_all_dollar_gesture_template (a_filename: READABLE_STRING_GENERAL)
			-- Save every dollar gesture templates loaded in the library
			-- inside the file located at a_filename. If it exists, the data will
			-- be added to the end. If it does not, it will be created.
			-- Note: The templates hashes are not saved in the file. You have to save
			-- them yourself for future utilisation
			-- (from GAME_DOLLAR_GESTURE_MANAGER)
		require -- from GAME_DOLLAR_GESTURE_MANAGER
			is_file_valid: attached (create {RAW_FILE}.make_with_name (a_filename)) as la_file implies ((la_file.exists implies la_file.is_access_writable) and (not la_file.exists implies la_file.is_creatable))
		local
			l_rwops: POINTER
			l_error: INTEGER_32
			l_filename_c, l_mode_c: C_STRING
			l_utf_converter: UTF_CONVERTER
		do
			create l_utf_converter
			create l_filename_c.make (l_utf_converter.string_32_to_utf_8_string_8 (a_filename.to_string_32))
			create l_mode_c.make ("ab")
			clear_error
			l_rwops := {GAME_SDL_EXTERNAL}.sdl_rwfromfile (l_filename_c.item, l_mode_c.item)
			if l_rwops.is_default_pointer then
				manage_error_pointer (l_rwops, "Cannot create the dollar template file.")
			else
				l_error := {GAME_SDL_EXTERNAL}.sdl_savealldollartemplates (l_rwops)
				manage_error_code (l_error - 1, "Cannot save the dollar templates")
				last_saved_dollar_gesture_template := l_error
				l_error := {GAME_SDL_EXTERNAL}.sdl_rwclose (l_rwops)
				if not has_error and l_error < 0 then
					manage_error_code (l_error, "Cannot close the dollar template file")
				end
			end
		end

	append_dollar_gesture_template (a_hash: INTEGER_64; a_filename: READABLE_STRING_GENERAL)
			-- Save the dollar gesture templates identified by a_hash
			-- inside the file located at a_filename. If it exists, the data will
			-- be added to the end. If it does not, it will be created.
			-- Note: The a_hash is not saved in the file. You have to save it
			-- yourself for future utilisation
			-- (from GAME_DOLLAR_GESTURE_MANAGER)
		require -- from GAME_DOLLAR_GESTURE_MANAGER
			is_file_valid: attached (create {RAW_FILE}.make_with_name (a_filename)) as la_file implies ((la_file.exists implies la_file.is_access_writable) and (not la_file.exists implies la_file.is_creatable))
		local
			l_rwops: POINTER
			l_error: INTEGER_32
			l_filename_c, l_mode_c: C_STRING
			l_utf_converter: UTF_CONVERTER
		do
			create l_utf_converter
			create l_filename_c.make (l_utf_converter.string_32_to_utf_8_string_8 (a_filename.to_string_32))
			create l_mode_c.make ("ab")
			clear_error
			l_rwops := {GAME_SDL_EXTERNAL}.sdl_rwfromfile (l_filename_c.item, l_mode_c.item)
			if l_rwops.is_default_pointer then
				manage_error_pointer (l_rwops, "Cannot create the dollar template file.")
			else
				l_error := {GAME_SDL_EXTERNAL}.sdl_savedollartemplate (a_hash, l_rwops)
				manage_error_code (l_error - 1, "Cannot save the dollar template")
				last_saved_dollar_gesture_template := l_error
				l_error := {GAME_SDL_EXTERNAL}.sdl_rwclose (l_rwops)
				if not has_error and l_error < 0 then
					manage_error_code (l_error, "Cannot close the dollar template file")
				end
			end
		end

	clear_events
			-- Remove common library event.
			-- Note: does not clear other events like window events, joystick events, etc. To clear every events
			-- in the system, used GAME_LIBRARY_CONTROLLER.clear_all_events.
			-- (from GAME_COMMON_EVENTS)
		local
			l_was_running: BOOLEAN
		do
			l_was_running := is_events_running
			if is_events_running then
				stop_events
			end
			quit_signal_actions_internal := Void
			iteration_actions_internal := Void
			joystick_founded_actions_internal := Void
			joystick_removed_actions_internal := Void
			joystick_found_actions_internal := Void
			joystick_remove_actions_internal := Void
			file_dropped_actions_internal := Void
			if l_was_running then
				run_events
			end
		ensure -- from GAME_EVENTS
			running_unchanged: is_events_running = old is_events_running
		end

	file_dropped_actions: ACTION_SEQUENCE [NATURAL_32, READABLE_STRING_GENERAL]
			-- Called when the file (or any other string) filename is drag and drop on a GAME_WINDOW.
			-- The event is not enabled by default. Use events_controller.enable_file_dropped_event to enable it.
			-- (from GAME_COMMON_EVENTS)
		require -- from GAME_COMMON_EVENTS
			joystick_found_event_enabled: events_controller.is_joy_device_founded_event_enable
		do
			if attached file_dropped_actions_internal as la_on_file_drop_internal then
				Result := la_on_file_drop_internal
			else
				create Result
				if is_events_running then
					events_controller.file_dropped_actions.extend (file_dropped_actions_callback)
				end
				file_dropped_actions_internal := Result
			end
		end

	generating_type: TYPE [detachable GAME_LIBRARY_CONTROLLER]
			-- Type of current object
			-- (type of which it is a direct instance)
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			generating_type_not_void: Result /= Void
		end

	generator: STRING_8
			-- Name of current object's generating class
			-- (base class of the type of which it is a direct instance)
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			generator_not_void: Result /= Void
			generator_not_empty: not Result.is_empty
		end

	has_error: BOOLEAN
			-- Is the library has generate an error
			-- (from GAME_ERROR_MANAGER)

	is_events_running: BOOLEAN assign set_is_running
			-- Is Current active
			-- (from GAME_EVENTS)

	iteration_actions: ACTION_SEQUENCE [NATURAL_32]
			-- Called at each game loop
			-- (from GAME_COMMON_EVENTS)
		do
			if attached iteration_actions_internal as la_on_iteration_internal then
				Result := la_on_iteration_internal
			else
				create Result
				if is_events_running then
					events_controller.iteration_actions.extend (iteration_actions_callback)
				end
				iteration_actions_internal := Result
			end
		end

	joystick_found_actions: ACTION_SEQUENCE [NATURAL_32, GAME_JOYSTICK]
			-- Called when a new joystick has been founded
			-- Automatically added to GAME_LIBRARY_CONTROLLER.joysticks
			-- (from GAME_COMMON_EVENTS)
		require -- from GAME_COMMON_EVENTS
			joystick_found_event_enabled: events_controller.is_joy_device_founded_event_enable
		do
			if attached joystick_found_actions_internal as la_on_joystick_added_internal then
				Result := la_on_joystick_added_internal
			else
				create Result
				if is_events_running then
					events_controller.joy_device_founded_actions.extend (joystick_founded_actions_callback)
				end
				joystick_found_actions_internal := Result
			end
		end

	joystick_founded_actions: ACTION_SEQUENCE [NATURAL_32, INTEGER_32]
		obsolete "Use `joystick_found_actions' instead [2020-03-30]"
			-- Called when a new joystick has been founded
			-- To get the new joystick, call GAME_LIBRARY_CONTROLLER.refresh_joysticks,
			-- then use the GAME_LIBRARY_CONTROLLER.joysticks.at(joystick_id)
			-- (from GAME_COMMON_EVENTS)
		require -- from GAME_COMMON_EVENTS
			joystick_found_event_enabled: events_controller.is_joy_device_founded_event_enable
		do
			if attached joystick_founded_actions_internal as la_on_joystick_added_internal then
				Result := la_on_joystick_added_internal
			else
				create Result
				if is_events_running then
					events_controller.joy_device_founded_actions.extend (joystick_founded_actions_callback)
				end
				joystick_founded_actions_internal := Result
			end
		end

	joystick_remove_actions: ACTION_SEQUENCE [NATURAL_32, GAME_JOYSTICK]
			-- Called when a new joystick has been removed
			-- The joystick will be removed from GAME_LIBRARY_CONTROLLER.joysticks after the
			-- calls of this feature.
			-- (from GAME_COMMON_EVENTS)
		require -- from GAME_COMMON_EVENTS
			joystick_remove_event_enabled: events_controller.is_joy_device_removed_event_enable
		do
			if attached joystick_remove_actions_internal as la_on_joystick_removed_internal then
				Result := la_on_joystick_removed_internal
			else
				create Result
				if is_events_running then
					events_controller.joy_device_removed_actions.extend (joystick_removed_actions_callback)
				end
				joystick_remove_actions_internal := Result
			end
		end

	joystick_removed_actions: ACTION_SEQUENCE [NATURAL_32, INTEGER_32]
		obsolete "Use `joystick_remove_actions' instead [2020-03-30]"
			-- Called when a new joystick has been removed
			-- The joystick will be removed from GAME_LIBRARY_CONTROLLER.joysticks after the
			-- calls of this feature.
			-- (from GAME_COMMON_EVENTS)
		require -- from GAME_COMMON_EVENTS
			joystick_remove_event_enabled: events_controller.is_joy_device_removed_event_enable
		do
			if attached joystick_removed_actions_internal as la_on_joystick_removed_internal then
				Result := la_on_joystick_removed_internal
			else
				create Result
				if is_events_running then
					events_controller.joy_device_removed_actions.extend (joystick_removed_actions_callback)
				end
				joystick_removed_actions_internal := Result
			end
		end

	last_error: READABLE_STRING_GENERAL
			-- The last error generate by the library
			-- (from GAME_SDL_ANY)
		local
			l_string: C_STRING
		do
			if is_manual_error then
				Result := Precursor {GAME_ERROR_MANAGER}
			else
				create l_string.make_by_pointer ({GAME_SDL_EXTERNAL}.sdl_geterror)
				Result := l_string.string
			end
		end

	last_loaded_dollar_gesture_template: INTEGER_32
			-- How many dollar gesture template has been load on the last
			-- call of the load_dollar_gesture_template feature
			-- (from GAME_DOLLAR_GESTURE_MANAGER)

	last_saved_dollar_gesture_template: INTEGER_32
			-- How many dollar gesture template has been save on the last
			-- call of the save_dollar_gesture_template,
			-- save_all_dollar_gesture_template, append_dollar_gesture_template
			-- or append_all_dollar_gesture_template feature
			-- (from GAME_DOLLAR_GESTURE_MANAGER)

	load_dollar_gesture_template (a_filename: READABLE_STRING_GENERAL)
			-- Load in Current every dollar gesture templates
			-- inside the file located at a_filename
			-- Note: The unique hash returned by the record_dollar_gesture
			-- feature has to be saved on the client side.
			-- (from GAME_DOLLAR_GESTURE_MANAGER)
		require -- from GAME_DOLLAR_GESTURE_MANAGER
			file_is_readable: attached (create {RAW_FILE}.make_with_name (a_filename)) as la_file implies (la_file.exists and then la_file.is_access_readable)
		local
			l_rwops: POINTER
			l_error: INTEGER_32
			l_filename_c, l_mode_c: C_STRING
		do
			create l_filename_c.make ({UTF_CONVERTER}.string_32_to_utf_8_string_8 (a_filename.to_string_32))
			create l_mode_c.make ("rb")
			clear_error
			l_rwops := {GAME_SDL_EXTERNAL}.sdl_rwfromfile (l_filename_c.item, l_mode_c.item)
			if l_rwops.is_default_pointer then
				manage_error_pointer (l_rwops, "Cannot open the dollar template file.")
			else
				l_error := {GAME_SDL_EXTERNAL}.sdl_loaddollartemplates (Dollar_gesture_template_index, l_rwops)
				if l_error < 0 then
					manage_error_code (l_error, "Cannot load the dollar template")
				elseif l_error = 0 then
					last_loaded_dollar_gesture_template := l_error
					put_manual_error ("The file does not seems to have a dollar template.", "Cannot load the dollar template")
				else
					last_loaded_dollar_gesture_template := l_error
				end
				l_error := {GAME_SDL_EXTERNAL}.sdl_rwclose (l_rwops)
				if not has_error and l_error < 0 then
					manage_error_code (l_error, "Cannot close the dollar template file")
				end
			end
		end

	quit_signal_actions: ACTION_SEQUENCE [NATURAL_32]
			-- When the application receive a quit signal.
			-- (from GAME_COMMON_EVENTS)
		require -- from GAME_COMMON_EVENTS
			quit_event_enabled: events_controller.is_quit_signal_event_enable
		do
			if attached quit_signal_actions_internal as la_on_quit_signal_internal then
				Result := la_on_quit_signal_internal
			else
				create Result
				if is_events_running then
					events_controller.quit_signal_actions.extend (quit_signal_actions_callback)
				end
				quit_signal_actions_internal := Result
			end
		end

	run_events
			-- Put Current active.
			-- (from GAME_COMMON_EVENTS)
		require -- from GAME_EVENTS
			run_not_already_running: not is_events_running
		do
			is_events_running := True
			if attached quit_signal_actions_internal as la_on_quit_signal_internal then
				events_controller.quit_signal_actions.extend (quit_signal_actions_callback)
			end
			if attached iteration_actions_internal as la_on_iteration_internal then
				events_controller.iteration_actions.extend (iteration_actions_callback)
			end
			if attached file_dropped_actions_internal as la_on_file_drop_internal then
				events_controller.file_dropped_actions.extend (file_dropped_actions_callback)
			end
		ensure -- from GAME_EVENTS
			is_running: is_events_running
		end

	save_all_dollar_gesture_template (a_filename: READABLE_STRING_GENERAL)
			-- Save every dollar gesture templates loaded in the library
			-- inside the file located at a_filename. If it exists, the file will be
			-- overwrited.
			-- Note: The templates hashes are not saved in the file. You have to save
			-- them yourself for future utilisation
			-- (from GAME_DOLLAR_GESTURE_MANAGER)
		require -- from GAME_DOLLAR_GESTURE_MANAGER
			is_file_creatable: (create {RAW_FILE}.make_with_name (a_filename)).is_creatable
		local
			l_rwops: POINTER
			l_error: INTEGER_32
			l_filename_c, l_mode_c: C_STRING
			l_utf_converter: UTF_CONVERTER
		do
			create l_utf_converter
			create l_filename_c.make (l_utf_converter.string_32_to_utf_8_string_8 (a_filename.to_string_32))
			create l_mode_c.make ("wb")
			clear_error
			l_rwops := {GAME_SDL_EXTERNAL}.sdl_rwfromfile (l_filename_c.item, l_mode_c.item)
			if l_rwops.is_default_pointer then
				manage_error_pointer (l_rwops, "Cannot create the dollar template file.")
			else
				l_error := {GAME_SDL_EXTERNAL}.sdl_savealldollartemplates (l_rwops)
				manage_error_code (l_error - 1, "Cannot save the dollar templates")
				last_saved_dollar_gesture_template := l_error
				l_error := {GAME_SDL_EXTERNAL}.sdl_rwclose (l_rwops)
				if not has_error and l_error < 0 then
					manage_error_code (l_error, "Cannot close the dollar template file")
				end
			end
		end

	save_dollar_gesture_template (a_hash: INTEGER_64; a_filename: READABLE_STRING_GENERAL)
			-- Save the dollar gesture templates identified by a_hash
			-- inside the file located at a_filename. If it exists, the file will be
			-- overwrited.
			-- Note: The a_hash is not saved in the file. You have to save it
			-- yourself for future utilisation
			-- (from GAME_DOLLAR_GESTURE_MANAGER)
		require -- from GAME_DOLLAR_GESTURE_MANAGER
			is_file_creatable: (create {RAW_FILE}.make_with_name (a_filename)).is_creatable
		local
			l_rwops: POINTER
			l_error: INTEGER_32
			l_filename_c, l_mode_c: C_STRING
			l_utf_converter: UTF_CONVERTER
		do
			create l_utf_converter
			create l_filename_c.make (l_utf_converter.string_32_to_utf_8_string_8 (a_filename.to_string_32))
			create l_mode_c.make ("wb")
			clear_error
			l_rwops := {GAME_SDL_EXTERNAL}.sdl_rwfromfile (l_filename_c.item, l_mode_c.item)
			if l_rwops.is_default_pointer then
				manage_error_pointer (l_rwops, "Cannot create the dollar template file.")
			else
				l_error := {GAME_SDL_EXTERNAL}.sdl_savedollartemplate (a_hash, l_rwops)
				manage_error_code (l_error - 1, "Cannot save the dollar template")
				last_saved_dollar_gesture_template := l_error
				l_error := {GAME_SDL_EXTERNAL}.sdl_rwclose (l_rwops)
				if not has_error and l_error < 0 then
					manage_error_code (l_error, "Cannot close the dollar template file")
				end
			end
		end

	set_is_running (a_value: BOOLEAN)
			-- Assign to is_running the value of a_value
			-- (from GAME_EVENTS)
		do
			if a_value then
				run_events
			else
				stop_events
			end
		ensure -- from GAME_EVENTS
			is_assign: is_events_running ~ a_value
		end

	stop_events
			-- Put Current innactive.
			-- (from GAME_COMMON_EVENTS)
		require -- from GAME_EVENTS
			stop_is_running: is_events_running
		do
			is_events_running := False;
			events_controller.quit_signal_actions.prune_all (quit_signal_actions_callback);
			events_controller.iteration_actions.prune_all (iteration_actions_callback);
			events_controller.file_dropped_actions.prune_all (file_dropped_actions_callback)
		ensure -- from GAME_EVENTS
			is_stopped: not is_events_running
		end
	
feature -- Comparison

	frozen deep_equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void
			-- or attached to isomorphic object structures?
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.is_deep_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			shallow_implies_deep: standard_equal (a, b) implies Result
			both_or_none_void: (a = Void) implies (Result = (b = Void))
			same_type: (Result and (a /= Void)) implies (b /= Void and then a.same_type (b))
			symmetric: Result implies deep_equal (b, a)
		end

	frozen equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void or attached
			-- to objects considered equal?
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.is_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.is_equal (b))
		end

	frozen is_deep_equal alias "≡≡≡" (other: GAME_LIBRARY_CONTROLLER): BOOLEAN
			-- Are Current and other attached to isomorphic object structures?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			shallow_implies_deep: standard_is_equal (other) implies Result
			same_type: Result implies same_type (other)
			symmetric: Result implies other.is_deep_equal (Current)
		end

	is_equal (other: GAME_LIBRARY_CONTROLLER): BOOLEAN
			-- Is other attached to an object considered
			-- equal to current object?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			symmetric: Result implies other ~ Current
			consistent: standard_is_equal (other) implies Result
		end

	frozen standard_equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void or attached to
			-- field-by-field identical objects of the same type?
			-- Always uses default object comparison criterion.
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.standard_is_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.standard_is_equal (b))
		end

	frozen standard_is_equal alias "≜" (other: GAME_LIBRARY_CONTROLLER): BOOLEAN
			-- Is other attached to an object of the same type
			-- as current object, and field-by-field identical to it?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			same_type: Result implies same_type (other)
			symmetric: Result implies other.standard_is_equal (Current)
		end
	
feature -- Status report

	conforms_to (other: ANY): BOOLEAN
			-- Does type of current object conform to type
			-- of other (as per Eiffel: The Language, chapter 13)?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		end

	same_type (other: ANY): BOOLEAN
			-- Is type of current object identical to type of other?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			definition: Result = (conforms_to (other) and other.conforms_to (Current))
		end
	
feature -- Duplication

	frozen clone (other: detachable ANY): like other
		obsolete "Use `twin' instead. [2017-05-31]"
			-- Void if other is void; otherwise new object
			-- equal to other
			--
			-- For non-void other, clone calls copy;
			-- to change copying/cloning semantics, redefine copy.
			-- (from ANY)
		do
			if other /= Void then
				Result := other.twin
			end
		ensure -- from ANY
			instance_free: class
			equal: Result ~ other
		end

	copy (other: GAME_LIBRARY_CONTROLLER)
			-- Update current object using fields of object attached
			-- to other, so as to yield equal objects.
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
			type_identity: same_type (other)
		external
			"built_in"
		ensure -- from ANY
			is_equal: Current ~ other
		end

	frozen deep_clone (other: detachable ANY): like other
		obsolete "Use `deep_twin' instead. [2017-05-31]"
			-- Void if other is void: otherwise, new object structure
			-- recursively duplicated from the one attached to other
			-- (from ANY)
		do
			if other /= Void then
				Result := other.deep_twin
			end
		ensure -- from ANY
			instance_free: class
			deep_equal: deep_equal (other, Result)
		end

	frozen deep_copy (other: GAME_LIBRARY_CONTROLLER)
			-- Effect equivalent to that of:
			--		copy (other . deep_twin)
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		do
			copy (other.deep_twin)
		ensure -- from ANY
			deep_equal: deep_equal (Current, other)
		end

	frozen deep_twin: GAME_LIBRARY_CONTROLLER
			-- New object structure recursively duplicated from Current.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			deep_twin_not_void: Result /= Void
			deep_equal: deep_equal (Current, Result)
		end

	frozen standard_clone (other: detachable ANY): like other
		obsolete "Use `standard_twin' instead. [2017-05-31]"
			-- Void if other is void; otherwise new object
			-- field-by-field identical to other.
			-- Always uses default copying semantics.
			-- (from ANY)
		do
			if other /= Void then
				Result := other.standard_twin
			end
		ensure -- from ANY
			instance_free: class
			equal: standard_equal (Result, other)
		end

	frozen standard_copy (other: GAME_LIBRARY_CONTROLLER)
			-- Copy every field of other onto corresponding field
			-- of current object.
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
			type_identity: same_type (other)
		external
			"built_in"
		ensure -- from ANY
			is_standard_equal: standard_is_equal (other)
		end

	frozen standard_twin: GAME_LIBRARY_CONTROLLER
			-- New object field-by-field identical to other.
			-- Always uses default copying semantics.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			standard_twin_not_void: Result /= Void
			equal: standard_equal (Result, Current)
		end

	frozen twin: GAME_LIBRARY_CONTROLLER
			-- New object equal to Current
			-- twin calls copy; to change copying/twinning semantics, redefine copy.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			twin_not_void: Result /= Void
			is_equal: Result ~ Current
		end
	
feature -- Basic operations

	frozen as_attached: attached GAME_LIBRARY_CONTROLLER
		obsolete "Remove calls to this feature. [2017-05-31]"
			-- Attached version of Current.
			-- (Can be used during transitional period to convert
			-- non-void-safe classes to void-safe ones.)
			-- (from ANY)
		do
			Result := Current
		end

	frozen default: detachable GAME_LIBRARY_CONTROLLER
			-- Default value of object's type
			-- (from ANY)
		do
		end

	frozen default_pointer: POINTER
			-- Default value of type POINTER
			-- (Avoid the need to write p.default for
			-- some p of type POINTER.)
			-- (from ANY)
		do
		ensure -- from ANY
			instance_free: class
		end

	default_rescue
			-- Process exception for routines with no Rescue clause.
			-- (Default: do nothing.)
			-- (from ANY)
		do
		end

	frozen do_nothing
			-- Execute a null action.
			-- (from ANY)
		do
		ensure -- from ANY
			instance_free: class
		end
	
feature {NONE} -- Implementation

	clear_error
			-- Remove error pending in Current
			-- (from GAME_SDL_ANY)
		require -- from  GAME_ERROR_MANAGER
			True
		do
			{GAME_SDL_EXTERNAL}.sdl_clearerror
			Precursor {GAME_ERROR_MANAGER}
			is_manual_error := False
		ensure -- from GAME_ERROR_MANAGER
			no_error: not has_error
		ensure then -- from GAME_SDL_ANY
			no_error: not is_manual_error
		end

	file_dropped_actions_callback: PROCEDURE [NATURAL_32, READABLE_STRING_GENERAL]
			-- Internal callback of the file dropped event
			-- (from GAME_COMMON_EVENTS)

	file_dropped_actions_internal: detachable ACTION_SEQUENCE [NATURAL_32, READABLE_STRING_GENERAL]
			-- Internal representation of the file drop event
			-- (from GAME_COMMON_EVENTS)

	is_manual_error: BOOLEAN
			-- Is the current pending error is a manual error (using manual_error as message)
			-- (from GAME_SDL_ANY)

	iteration_actions_callback: PROCEDURE [NATURAL_32]
			-- Internal callback of the iteration event
			-- (from GAME_COMMON_EVENTS)

	iteration_actions_internal: detachable ACTION_SEQUENCE [NATURAL_32]
			-- Internal representation of the iteration event
			-- (from GAME_COMMON_EVENTS)

	joystick_found_actions_internal: detachable ACTION_SEQUENCE [NATURAL_32, GAME_JOYSTICK]
			-- Internal representation of the joystick founded event
			-- (from GAME_COMMON_EVENTS)

	joystick_founded_actions_callback: PROCEDURE [NATURAL_32, INTEGER_32]
			-- Internal callback of the joystick founded event
			-- (from GAME_COMMON_EVENTS)

	joystick_founded_actions_internal: detachable ACTION_SEQUENCE [NATURAL_32, INTEGER_32]
			-- Internal representation of the joystick founded event
			-- (from GAME_COMMON_EVENTS)

	joystick_remove_actions_internal: detachable ACTION_SEQUENCE [NATURAL_32, GAME_JOYSTICK]
			-- Internal representation of the joystick removed event
			-- (from GAME_COMMON_EVENTS)

	joystick_removed_actions_callback: PROCEDURE [NATURAL_32, INTEGER_32]
			-- Internal callback of the joystick removed event
			-- (from GAME_COMMON_EVENTS)

	joystick_removed_actions_internal: detachable ACTION_SEQUENCE [NATURAL_32, INTEGER_32]
			-- Internal representation of the joystick removed event
			-- (from GAME_COMMON_EVENTS)

	manage_error_boolean (a_boolean: BOOLEAN; a_message: READABLE_STRING_GENERAL)
			-- Create an error if a_boolean is false.
			-- If there is an error, append a_message to the error message
			-- on the SDL2 library
			-- (from GAME_SDL_ANY)
		do
			if not a_boolean then
				if Print_on_error_internal.item then
					Io.Error.put_string (a_message.to_string_8 + "%N");
					Io.Error.put_string (last_error.to_string_8 + "%N")
				end
				has_error := True
			end
		ensure -- from GAME_SDL_ANY
				not a_boolean implies has_error
		end

	manage_error_code (a_error_code: INTEGER_32; a_message: READABLE_STRING_GENERAL)
			-- If needed create an error depending of the error code a_code.
			-- If there is an error, append a_message to the error message
			-- on the SDL2 library
			-- (from GAME_SDL_ANY)
		do
			if a_error_code < 0 then
				if Print_on_error_internal.item then
					Io.Error.put_string (a_message.to_string_8 + "%N");
					Io.Error.put_string (last_error.to_string_8 + "%N")
				end
				has_error := True
			end
		end

	manage_error_pointer (a_pointer: POINTER; a_message: READABLE_STRING_GENERAL)
			-- Create an error if a_pointer is not valid.
			-- If there is an error, append a_message to the error message
			-- on the SDL2 library
			-- (from GAME_SDL_ANY)
		do
			if a_pointer.is_default_pointer then
				if Print_on_error_internal.item then
					Io.Error.put_string (a_message.to_string_8 + "%N");
					Io.Error.put_string (last_error.to_string_8 + "%N")
				end
				has_error := True
			end
		ensure -- from GAME_SDL_ANY
				a_pointer.is_default_pointer implies has_error
		end

	manual_error: detachable READABLE_STRING_GENERAL
			-- The specific message for the last error
			-- (from GAME_ERROR_MANAGER)

	Print_on_error_internal: CELL [BOOLEAN]
			-- True when an error occured,
			-- The library will print it right away.
			-- (from GAME_ERROR_MANAGER)
		once ("PROCESS")
			create Result.put (True)
		end

	put_manual_error (a_general_message, a_specific_error: READABLE_STRING_GENERAL)
			-- Create an error using a_general_error for the debug information
			-- and a_specific_error for the lasting information
			-- (from GAME_SDL_ANY)
		do
			is_manual_error := True
			Precursor {GAME_ERROR_MANAGER} (a_general_message, a_specific_error)
		ensure -- from GAME_ERROR_MANAGER
				has_error
		end

	quit_signal_actions_callback: PROCEDURE [NATURAL_32]
			-- Internal callback of the quit signal event
			-- (from GAME_COMMON_EVENTS)

	quit_signal_actions_internal: detachable ACTION_SEQUENCE [NATURAL_32]
			-- Internal representation of the quit signal event
			-- (from GAME_COMMON_EVENTS)
	
feature -- Implementation

	disable_print_on_error
			-- Desactive the print_on_error functionnality.
			-- (from GAME_ERROR_MANAGER)
		do
			Print_on_error_internal.put (False)
		end

	enable_print_on_error
			-- Active the print_on_error functionnality.
			-- (from GAME_ERROR_MANAGER)
		do
			Print_on_error_internal.put (True)
		end

	print_on_error: BOOLEAN
			-- When an error occured, the library will print
			-- informations about the error on the error console
			-- output (default is True).
			-- (from GAME_ERROR_MANAGER)
		do
			Result := Print_on_error_internal.item
		end

	set_print_on_error (a_value: BOOLEAN)
			-- Assign to print_on_error the value of a_value
			-- (from GAME_ERROR_MANAGER)
		do
			if a_value then
				enable_print_on_error
			else
				disable_print_on_error
			end
		ensure -- from GAME_ERROR_MANAGER
			is_assign: print_on_error ~ a_value
		end
	
feature {GAME_SDL_ANY} 

	internal_windows: LIST [GAME_WINDOW]
			-- Every GAME_WINDOW of the system.
	
feature {NONE} -- Haptic implementation

	close_all_haptics
			-- Close the haptic that has been opened
		require
			controller_close_all_haptics_haptic_enabled: is_haptic_enable
			close_all_haptic_attach: internal_haptics /= Void
		do
			internal_haptics.do_all (agent (a_haptic: GAME_HAPTIC_DEVICE)
					do
						if a_haptic.is_open then
							a_haptic.close
						end
					end)
		end

	internal_haptics: ARRAYED_LIST [GAME_HAPTIC_DEVICE]
			-- Every GAME_HAPTIC connected to the system.

	open_all_haptic
			-- Open all haptic that is not already open.
		require
			haptic_is_enabled: is_haptic_enable
		do
			internal_haptics.do_all (agent (a_haptic: GAME_HAPTIC_DEVICE)
					do
						if not a_haptic.is_open then
							a_haptic.open
						end
					end)
		end
	
feature -- Haptic methods

	haptic_maximum_gain: INTEGER_32 assign set_haptic_maximum_gain
			-- The maximum gain used by haptics in the system.
			-- The GAME_HAPTIC.set_gain always take 0-100
			-- gain value, but the real value is scaled
		require
			haptic_enabled: is_haptic_enable
		local
			l_value_text: READABLE_STRING_GENERAL
		do
			l_value_text := library_variable ("SDL_HAPTIC_GAIN_MAX")
			Result := 100
			if l_value_text.is_integer_32 then
				Result := l_value_text.to_integer_32
				if Result < 0 then
					Result := 0
				elseif Result > 100 then
					Result := 100
				end
			end
		ensure
			result_valid: Result >= 0 and Result <= 100
		end

	haptics: CHAIN_INDEXABLE_ITERATOR [GAME_HAPTIC_DEVICE]
			-- Every haptic devices on the system
		require
			haptic_is_haptic_enabled: is_haptic_enable
		do
			create Result.make (internal_haptics)
		end

	refresh_haptics
			-- Update the haptics list (if haptics as been add or remove)
			-- Warning: This will close all opened haptics
		require
			controller_update_haptics_haptic_enabled: is_haptic_enable
		local
			i, l_haptic_count: INTEGER_32
		do
			close_all_haptics;
			internal_haptics.wipe_out
			l_haptic_count := {GAME_SDL_EXTERNAL}.sdl_numhaptics
			from
				i := 0
			until
				i >= l_haptic_count
			loop
				internal_haptics.extend (create {GAME_HAPTIC_DEVICE}.make (i))
				i := i + 1
			end
		end

	set_haptic_maximum_gain (a_gain: INTEGER_32)
			-- Assign haptic_maximum_gain with the value of a_gain
		require
			haptic_enabled: is_haptic_enable
			gain_is_valid: a_gain >= 0 and a_gain <= 100
		do
			set_library_variable ("SDL_HAPTIC_GAIN_MAX", a_gain.out)
		ensure
			is_assign: not has_error implies haptic_maximum_gain = a_gain
		end
	
feature {NONE} -- Implementation - Methods

	initialise_sub_system (a_flags: NATURAL_32)
			-- Initialise SDL sub-systems defined by a_flags.
		local
			l_error: INTEGER_32
		do
			clear_error
			l_error := {GAME_SDL_EXTERNAL}.sdl_initsubsystem (a_flags)
			manage_error_code (l_error, "Cannot initialize library sub system.")
		end

	is_sub_system_enable (a_flags: NATURAL_32): BOOLEAN
			-- Return true if the sub-systems defined by the a_flags are enable.
		do
			Result := {GAME_SDL_EXTERNAL}.sdl_wasinit (a_flags) = a_flags
		end

	quit_sub_system (a_flags: NATURAL_32)
			-- Disable all SDL sub-system defined by a_flags.
		do
			{GAME_SDL_EXTERNAL}.sdl_quitsubsystem (a_flags)
		end
	
feature {NONE} -- Implementation - Variables

	Instance_count: CELL [INTEGER_32]
			-- The number of time the Currents class has been created
			-- Since it is a singleton, it must be always 0 or 1
		once ("PROCESS")
			create Result.put (0)
		end

	internal_mouse_haptic: detachable GAME_HAPTIC_MOUSE
			-- The haptic mouse

	internal_preference_path: detachable PATH
			-- internal storage for the preference_path attribute

	last_tick: NATURAL_32
			-- The time_since_create value on the last iteration

	must_stop: BOOLEAN
			-- When true, the launching process must stop

	ticks_per_iteration: NATURAL_32
			-- Minimum number of ticks to pass between each iteration
	
feature {NONE} -- Initialisation

	make_events
			-- Initialization of Current
			-- (from GAME_COMMON_EVENTS)
		do
			file_dropped_actions_callback := agent (a_timestamp: NATURAL_32; a_filename: READABLE_STRING_GENERAL)
				do
					file_dropped_actions.call ([a_timestamp, a_filename])
				end
			joystick_founded_actions_callback := agent (a_timestamp: NATURAL_32; a_joystick_id: INTEGER_32)
				do
					manage_joystick_founded_callback (a_timestamp, a_joystick_id)
				end
			joystick_removed_actions_callback := agent (a_timestamp: NATURAL_32; a_joystick_id: INTEGER_32)
				do
					manage_joystick_removed_callback (a_timestamp, a_joystick_id)
				end
			quit_signal_actions_callback := agent (a_timestamp: NATURAL_32)
				do
					quit_signal_actions.call ([a_timestamp])
				end
			iteration_actions_callback := agent (a_timestamp: NATURAL_32)
				do
					iteration_actions.call ([a_timestamp])
				end
			Precursor {GAME_EVENTS}
		ensure -- from GAME_EVENTS
			make_event_is_running: is_events_running
		end
	
feature {NONE} -- Joystick implementation

	close_all_joysticks
			-- Close the joystick that has been opened
		require
			controller_close_all_joysticks_joystick_enabled: is_joystick_enable
			close_all_joystick_attach: internal_joysticks /= Void
		do
			internal_joysticks.do_all (agent (a_joystick: detachable GAME_JOYSTICK)
					do
						if attached a_joystick as la_joystick and then la_joystick.is_open then
							la_joystick.close
						end
					end)
		end

	initialise_joysticks
			-- Fill internal_joysticks
		require
			controller_update_joysticks_joystick_enabled: is_joystick_enable
		local
			i, l_joystick_count: INTEGER_32
		do
		end

	internal_joysticks: ARRAYED_LIST [detachable GAME_JOYSTICK]
			-- Every GAME_JOYSTICK connected to the system.

	manage_joystick_founded_callback (a_timestamp: NATURAL_32; a_joystick_id: INTEGER_32)
			-- {PRECURSOR}
		local
			l_joystick: GAME_JOYSTICK
		do
			across
				internal_joysticks as la_joysticks
			loop
				if attached la_joysticks.item as la_joystick and then la_joystick.open_index ~ a_joystick_id then
					l_joystick := la_joystick
				end
			end
			if not attached l_joystick then
				create l_joystick.make (a_joystick_id);
				internal_joysticks.extend (l_joystick);
				joystick_founded_actions.call ([a_timestamp, a_joystick_id]);
				joystick_found_actions.call ([a_timestamp, l_joystick])
			end
		end

	manage_joystick_removed_callback (a_timestamp: NATURAL_32; a_joystick_id: INTEGER_32)
			-- {PRECURSOR}
		local
			l_joystick: GAME_JOYSTICK
			l_index: INTEGER_32
			l_cursor: ARRAYED_LIST_ITERATION_CURSOR [detachable GAME_JOYSTICK]
			l_found: BOOLEAN
		do
			joystick_removed_actions.call ([a_timestamp, a_joystick_id])
			from
				l_cursor := internal_joysticks.new_cursor
				l_index := 1
			until
				l_cursor.after or l_found
			loop
				if attached l_cursor.item as la_item and then la_item.index = a_joystick_id then
					l_found := True
					manage_joystick_removed_joystick (a_timestamp, l_index)
				end
				l_index := l_index + 1;
				l_cursor.forth
			end
			if not l_found then
				manage_joystick_removed_joystick (a_timestamp, a_joystick_id + 1)
			end
		end

	manage_joystick_removed_joystick (a_timestamp: NATURAL_32; a_index: INTEGER_32)
			-- Remove the GAME_JOYSTICK at index a_index in internal_joysticks
		local
			l_open_index: INTEGER_32
		do
			if internal_joysticks.valid_index (a_index) and then attached internal_joysticks.at (a_index) as la_joystick then
				joystick_remove_actions.call ([a_timestamp, la_joystick]);
				la_joystick.remove;
				la_joystick.internal_close
				if la_joystick.is_events_running then
					la_joystick.stop_events
				end;
				la_joystick.clear_events
				l_open_index := la_joystick.open_index
				internal_joysticks.at (a_index) := Void
				from
					internal_joysticks.move (a_index)
				until
					internal_joysticks.exhausted
				loop
					if attached internal_joysticks.item as la_next_joystick then
						la_next_joystick.set_open_index (l_open_index)
						l_open_index := l_open_index + 1
					end;
					internal_joysticks.forth
				end
			end
		end

	open_all_joystick
			-- Open all joystick that is not already open.
		require
			joysticks_is_enabled: is_joystick_enable
		do
			internal_joysticks.do_all (agent (a_joystick: detachable GAME_JOYSTICK)
					do
						if attached a_joystick as la_joystick and then not la_joystick.is_open then
							la_joystick.open
						end
					end)
		end
	
feature -- Joystick methods

	joysticks: CHAIN_INDEXABLE_ITERATOR [GAME_JOYSTICK]
			-- Every GAME_JOYSTICK detected by Current
		require
			joysticks_is_joystick_enabled: is_joystick_enable
		local
			l_joysticks: LINKED_LIST [GAME_JOYSTICK]
		do
			create l_joysticks.make
			across
				internal_joysticks as la_joysticks
			loop
				if attached la_joysticks.item as la_joystick then
					l_joysticks.extend (la_joystick)
				end
			end
			create Result.make (l_joysticks)
		end

	refresh_joysticks
		obsolete "Should not be necessary anymore [2020-03-30]"
			-- Update the joystiks list (if joysticks as been add or remove)
			-- Warning: This will close all opened joysticks
		require
			controller_update_joysticks_joystick_enabled: is_joystick_enable
		do
		end

	update_joysticks_state
			-- Update the state of all opened joystick. This procedure is
			-- Called at each game loop instead you disable every joystick event
			-- with GAME_EVENTS_CONTROLLER.disable_joy_*_event or with
			-- GAME_EVENTS_CONTROLLER.disable_every_joy_events
		do
			{GAME_SDL_EXTERNAL}.sdl_joystickupdate
		end
	
feature -- Mouse		

	cursor: GAME_CURSOR
			-- The GAME_CURSOR presently used in Current
		do
			create Result.make_by_pointer ({GAME_SDL_EXTERNAL}.sdl_getcursor)
		end

	default_cursor: GAME_CURSOR
			-- The GAME_CURSOR used in the creation of Current
		do
			create Result.make_by_pointer ({GAME_SDL_EXTERNAL}.sdl_getdefaultcursor)
		end

	disable_relative_mouse
			-- Unset the is_relative_mouse_enabled mode
		do
			set_is_relative_mouse_enabled (False)
		ensure
			is_assign: not has_error implies not is_relative_mouse_enabled
		end

	enable_relative_mouse
			-- Set the is_relative_mouse_enabled mode
		do
			set_is_relative_mouse_enabled (True)
		ensure
			is_assign: not has_error implies is_relative_mouse_enabled
		end

	hide_mouse_cursor
			-- Don't show the mouse cursor when the mouse is on a window created by the library.
		local
			l_error: INTEGER_32
		do
			l_error := {GAME_SDL_EXTERNAL}.sdl_showcursor ({GAME_SDL_EXTERNAL}.sdl_disable)
		ensure
			hide_mouse_cursor_valid: not is_cursor_visible
		end

	is_cursor_visible: BOOLEAN assign set_is_cursor_visible
			-- Return true if the library is set to show the mous cursor when the mouse is on a window created by the library.
		local
			l_is_enable: INTEGER_32
		do
			l_is_enable := {GAME_SDL_EXTERNAL}.sdl_showcursor ({GAME_SDL_EXTERNAL}.sdl_query)
			check
					l_is_enable = {GAME_SDL_EXTERNAL}.sdl_enable or l_is_enable = {GAME_SDL_EXTERNAL}.sdl_disable
			end
			Result := l_is_enable = {GAME_SDL_EXTERNAL}.sdl_enable
		end

	is_relative_mouse_enabled: BOOLEAN assign set_is_relative_mouse_enabled
			-- While Enabled, the cursor is hidden, and only relative motion events (delta_x and delta_y)
			-- will be delivered to the current window the mouse position will not change.
			-- Note: This function will flush any pending mouse motion.
		do
			Result := {GAME_SDL_EXTERNAL}.sdl_getrelativemousemode
		end

	redraw_cursor
			-- Redraw the cursor
		do
			{GAME_SDL_EXTERNAL}.sdl_setcursor (create {POINTER})
		end

	set_cursor (a_cursor: GAME_CURSOR)
			-- Assign cursor with the value of a_cursor
		require
			cursor_valid: a_cursor.exists
		do
			clear_error
			{GAME_SDL_EXTERNAL}.sdl_setcursor (a_cursor.item)
			manage_error_boolean (cursor ~ a_cursor, "Cannot set the cursor.")
		ensure
			is_assign: not has_error implies cursor ~ a_cursor
		end

	set_default_cursor
			-- Put back the default GAME_CURSOR
		do
			set_cursor (default_cursor)
		end

	set_is_cursor_visible (a_value: BOOLEAN)
			-- Assign to is_cursor_visible the value of a_value
		do
			if a_value then
				show_mouse_cursor
			else
				hide_mouse_cursor
			end
		ensure
			is_assign: is_cursor_visible ~ a_value
		end

	set_is_relative_mouse_enabled (a_value: BOOLEAN)
			-- Assign is_relative_mouse_enabled with the value of a_value
		local
			l_error: INTEGER_32
		do
			clear_error
			l_error := {GAME_SDL_EXTERNAL}.sdl_setrelativemousemode (a_value)
			manage_error_code (l_error, "Relative mouse mode not supported.")
		ensure
			is_assign: not has_error implies is_relative_mouse_enabled ~ a_value
		end

	show_mouse_cursor
			-- Show the mouse cursor when the mouse is on a window created by the library.
		local
			l_error: INTEGER_32
		do
			l_error := {GAME_SDL_EXTERNAL}.sdl_showcursor ({GAME_SDL_EXTERNAL}.sdl_enable)
		ensure
			show_mouse_cursor_valid: is_cursor_visible
		end
	
feature -- OpenGL

	disable_gl
			-- Unload the OpenGL library
		require
			is_gl_enabled: is_gl_enabled
		do
			across
				windows as la_windows
			loop
				if attached {GAME_WINDOW_GL} la_windows.item then
					la_windows.item.close
				end
			end
			{MEMORY}.full_collect
			{GAME_SDL_EXTERNAL}.sdl_gl_unloadlibrary
			is_gl_enabled := False
		end

	enable_gl
			-- Load the OpenGL library
		require
			not_already_enabled: not is_gl_enabled
			video_must_be_loaded: is_video_enable
		local
			l_error: INTEGER_32
		do
			l_error := {GAME_SDL_EXTERNAL}.sdl_gl_loadlibrary (create {POINTER})
			is_gl_enabled := l_error = 0
			manage_error_code (l_error, "Cannot enable the GL library")
		end

	enable_gl_from_file (a_file_name: READABLE_STRING_GENERAL)
			-- Load the OpenGL library contained in file at a_file_name
		require
			not_already_enabled: not is_gl_enabled
			video_must_be_loaded: is_video_enable
			file_valid: attached (create {RAW_FILE}.make_with_name (a_file_name)) as la_file and then (la_file.exists and la_file.is_access_readable)
		local
			l_error: INTEGER_32
			l_c_file_name: C_STRING
		do
			create l_c_file_name.make (a_file_name)
			l_error := {GAME_SDL_EXTERNAL}.sdl_gl_loadlibrary (l_c_file_name.item)
			is_gl_enabled := l_error = 0
			manage_error_code (l_error, "Cannot enable the GL library")
		end

	is_gl_enabled: BOOLEAN
			-- The OpenGL library has been loaded
	
feature -- Other methods

	base_path: PATH
			-- The PATH of the executable. This PATH is not safe for writing file.
		local
			l_path_c: C_STRING
			l_pointer: POINTER
		do
			l_pointer := {GAME_SDL_EXTERNAL}.sdl_getbasepath
			create l_path_c.make_shared_from_pointer (l_pointer)
			create Result.make_from_string ({UTF_CONVERTER}.utf_8_string_8_to_escaped_string_32 (l_path_c.string))
			{GAME_SDL_EXTERNAL}.sdl_free (l_pointer)
		end

	clear_all_events
			-- Clear every events set in the system
		do
			events_controller.clear
			across
				internal_joysticks as la_joysticks
			loop
				if attached la_joysticks.item as la_joystick then
					la_joystick.clear_events
				end
			end
			across
				internal_windows as la_windows
			loop
				la_windows.item.clear_events
			end
			if attached internal_touch_devices as la_devices then
				across
					la_devices as lla_devices
				loop
					lla_devices.item.clear_events
				end
			end
		end

	delay (a_millisecond: NATURAL_32)
			-- Pause the execution for given time in millisecond.
		do
			{GAME_SDL_EXTERNAL}.sdl_delay (a_millisecond)
		end

	events_controller: GAME_EVENTS_CONTROLLER
			-- Manage every internal events

	get_preference_path (a_organisation, a_application_name: READABLE_STRING_GENERAL)
			-- Retreive the preference_path. Create the directory if it does not already exist.
		local
			l_converter: UTF_CONVERTER
			l_path_c, l_organisation_c, l_application_name_c: C_STRING
			l_pointer: POINTER
		do
			create l_converter
			create l_organisation_c.make (l_converter.utf_32_string_to_utf_8_string_8 (a_organisation.to_string_32))
			create l_application_name_c.make (l_converter.utf_32_string_to_utf_8_string_8 (a_application_name.to_string_32))
			l_pointer := {GAME_SDL_EXTERNAL}.sdl_getprefpath (l_organisation_c.item, l_application_name_c.item)
			create l_path_c.make_shared_from_pointer (l_pointer)
			create internal_preference_path.make_from_string (l_converter.utf_8_string_8_to_escaped_string_32 (l_path_c.string))
			{GAME_SDL_EXTERNAL}.sdl_free (l_pointer)
		end

	is_preference_path_retreived: BOOLEAN
			-- Is preference_path has been correctly retreived by get_preference_path
		do
			Result := attached internal_preference_path
		end

	iteration_per_second: NATURAL_32 assign set_iteration_per_second
			-- An approximation of the number of event loop iteration per second.
			-- Not used by launch_no_delay
		do
			Result := 1000 // ticks_per_iteration
		end

	launch
			-- Start the main loop. Used to get a Event-driven programming only.
			-- Don't forget to execute the method stop in an event handeler.
		local
			l_delay: INTEGER_64
		do
			from
				must_stop := False
				last_tick := time_since_create
			until
				must_stop
			loop
				update_events
				l_delay := ticks_per_iteration.to_integer_64
				l_delay := l_delay - (time_since_create - last_tick).to_integer_32.to_integer_64
				if l_delay < 1 then
					delay (1)
				else
					delay (l_delay.to_natural_32)
				end
				last_tick := time_since_create
			end
		end

	launch_no_delay
			-- Start the main loop without any loop delay. Use it if you have
			-- a synchronisation system (like vsync) included inside the event handler
			-- Used to get a Event-driven programming only.
			-- Don't forget to execute the method stop in an event handler.
		do
			from
				must_stop := False
			until
				must_stop
			loop
				update_events
			end
		end

	launch_with_iteration_per_second (a_iteration_per_second: NATURAL_32)
			-- Start the main loop. Used to get a Event-driven programming only.
			-- Don't forget to execute the method stop in an event handeler.
			-- Set iteration_per_second to a_iteration_per_second before launching.
		do
			set_iteration_per_second (a_iteration_per_second)
			launch
		end

	library_variable (a_variable: READABLE_STRING_GENERAL): READABLE_STRING_GENERAL assign set_library_variable
			-- Retreive the internal variable a_variable or an empty text if it does not exist.
		local
			l_text_ptr: POINTER
		do
			l_text_ptr := {GAME_SDL_EXTERNAL}.sdl_getenv ((create {C_STRING}.make (a_variable)).item)
			if l_text_ptr.is_default_pointer then
				Result := ""
			else
				Result := (create {C_STRING}.make_by_pointer (l_text_ptr)).string
			end
		end

	mouse_haptic: GAME_HAPTIC_MOUSE
			-- The haptic device inside the mouse
		require
			mouse_has_haptic: mouse_has_haptic
		do
			if attached internal_mouse_haptic as la_internal_mouse_haptic then
				Result := la_internal_mouse_haptic
			else
				create Result.make
				internal_mouse_haptic := Result
			end
		end

	mouse_has_haptic: BOOLEAN
			-- Has the mouse have an internal haptic device
		do
			Result := {GAME_SDL_EXTERNAL}.sdl_mouseishaptic
		end

	preference_path: PATH
			-- The PATH to save preference files. Is retreived (or created) by get_preference_path
			-- The directory pointed by this PATH should be writable
			-- Do not use the base_path to write files
		require
			path_retreived: is_preference_path_retreived
		do
			if attached internal_preference_path as la_path then
				Result := la_path
			else
				create Result.make_empty
			end
		end

	quit_library
			-- Close the library. Must be used before the end of the application
		do
			internal_windows.wipe_out
			if is_gl_enabled then
				disable_gl
			end
			clear_events
			if is_joystick_enable then
				disable_joystick
			end
			refresh_touch_devices
			create events_controller;
			events_controller.set_game_library (Current)
			{MEMORY}.full_collect
			{GAME_SDL_EXTERNAL}.sdl_quit_lib
		end

	set_iteration_per_second (a_iteration_per_second: NATURAL_32)
			-- Set iteration_per_second to a_iteration_per_second
			-- Note that this is an aproximation.
		do
			ticks_per_iteration := 1000 // a_iteration_per_second
		end

	set_library_variable (a_variable, a_value: READABLE_STRING_GENERAL)
			-- Assign the internal variable a_variable with the value a_value.
		local
			l_c_name, l_c_value: C_STRING
			l_error: INTEGER_32
		do
			create l_c_name.make (a_variable)
			create l_c_value.make (a_value)
			l_error := {GAME_SDL_EXTERNAL}.sdl_setenv (l_c_name.item, l_c_value.item, True)
			manage_error_code (l_error, {STRING_32}"Cannot set the environment variable " + a_variable.to_string_32 + {STRING_32}" with value " + a_value.to_string_32 + {STRING_32}".")
		end

	stop
			-- Stop the main loop
		do
			must_stop := True
		end

	time_since_create: NATURAL_32
			-- Number of millisecond since the initialisation of the library.
		do
			Result := {GAME_SDL_EXTERNAL}.sdl_getticks
		end

	update_events
			-- Execute the event polling and throw the event handeler execution for each event.
		do
			events_controller.poll_event
		end
	
feature -- Output

	Io: STD_FILES
			-- Handle to standard file setup
			-- (from ANY)
		once
			create Result;
			Result.set_output_default
		ensure -- from ANY
			instance_free: class
			io_not_void: Result /= Void
		end

	out: STRING_8
			-- New string containing terse printable representation
			-- of current object
			-- (from ANY)
		do
			Result := tagged_out
		ensure -- from ANY
			out_not_void: Result /= Void
		end

	print (o: detachable ANY)
			-- Write terse external representation of o
			-- on standard output.
			-- (from ANY)
		local
			s: READABLE_STRING_8
		do
			if attached o then
				s := o.out
				if attached {READABLE_STRING_32} s as s32 then
					Io.put_string_32 (s32)
				elseif attached {READABLE_STRING_8} s as s8 then
					Io.put_string (s8)
				else
					Io.put_string_32 (s.as_string_32)
				end
			end
		ensure -- from ANY
			instance_free: class
		end

	frozen tagged_out: STRING_8
			-- New string containing terse printable representation
			-- of current object
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			tagged_out_not_void: Result /= Void
		end
	
feature -- Platform

	Operating_environment: OPERATING_ENVIRONMENT
			-- Objects available from the operating system
			-- (from ANY)
		once
			create Result
		ensure -- from ANY
			instance_free: class
			operating_environment_not_void: Result /= Void
		end
	
feature {NONE} -- Retrieval

	frozen internal_correct_mismatch
			-- Called from runtime to perform a proper dynamic dispatch on correct_mismatch
			-- from MISMATCH_CORRECTOR.
			-- (from ANY)
		local
			l_msg: STRING_32
			l_exc: EXCEPTIONS
		do
			if attached {MISMATCH_CORRECTOR} Current as l_corrector then
				l_corrector.correct_mismatch
			else
				create l_msg.make_from_string ("Mismatch: ".as_string_32)
				create l_exc;
				l_msg.append (generating_type.name_32);
				l_exc.raise_retrieval_exception (l_msg)
			end
		end
	
feature -- Subs Systems

	disable_events
			-- Disable the events fonctionality
		require
			sdl_controller_disable_events_not_enabled: is_events_enable
		do
			quit_sub_system ({GAME_SDL_EXTERNAL}.sdl_init_events)
		ensure
			sdl_controller_disable_events_disabled: not is_events_enable
		end

	disable_haptic
			-- Disable the haptic (force feedback) fonctionality
		require
			sdl_controller_disable_haptic_not_enabled: is_haptic_enable
		do
			close_all_haptics;
			internal_haptics.wipe_out
			quit_sub_system ({GAME_SDL_EXTERNAL}.sdl_init_haptic)
		ensure
			sdl_controller_disable_haptic_disabled: not is_haptic_enable
		end

	disable_joystick
			-- Disable the joystick fonctionality
		require
			sdl_controller_disable_joystick_not_enabled: is_joystick_enable
		do
			events_controller.joy_device_founded_actions.prune_all (joystick_founded_actions_callback);
			events_controller.joy_device_removed_actions.prune_all (joystick_removed_actions_callback)
			close_all_joysticks;
			internal_joysticks.wipe_out
			quit_sub_system ({GAME_SDL_EXTERNAL}.sdl_init_joystick)
		ensure
			sdl_controller_disable_joystick_disabled: not is_joystick_enable
		end

	disable_video
			-- Disable the video functionalities
		require
			sdl_controller_disable_video_not_enabled: is_video_enable
		do
			across
				windows as la_windows
			loop
				la_windows.item.close
			end
			{MEMORY}.full_collect
			quit_sub_system ({GAME_SDL_EXTERNAL}.sdl_init_video)
		ensure
			sdl_controller_disable_video_disabled: not is_video_enable
		end

	enable_events
			-- Unable the events functionality.
		require
			sdl_controller_enable_events_already_enabled: not is_events_enable
		do
			initialise_sub_system ({GAME_SDL_EXTERNAL}.sdl_init_events)
		ensure
			sdl_controller_enable_events_enabled: is_events_enable
		end

	enable_haptic
			-- Unable the haptic (force feedback) functionality.
			-- Often use for Controller rumble.
		require
			sdl_controller_enable_haptic_already_enabled: not is_haptic_enable
		do
			initialise_sub_system ({GAME_SDL_EXTERNAL}.sdl_init_haptic)
			refresh_haptics
		ensure
			sdl_controller_enable_haptic_enabled: is_haptic_enable
		end

	enable_joystick
			-- Unable the joystick functionality
		require
			sdl_controller_enable_joystick_already_enabled: not is_joystick_enable
		do
			initialise_sub_system ({GAME_SDL_EXTERNAL}.sdl_init_joystick)
			initialise_joysticks;
			events_controller.joy_device_founded_actions.extend (joystick_founded_actions_callback);
			events_controller.joy_device_removed_actions.extend (joystick_removed_actions_callback)
		ensure
			sdl_controller_enable_joystick_enabled: is_joystick_enable
		end

	enable_video
			-- Unable the video functionalities
		require
			sdl_controller_enable_video_already_enabled: not is_video_enable
		do
			initialise_sub_system ({GAME_SDL_EXTERNAL}.sdl_init_video)
		ensure
			sdl_controller_enable_video_enabled: is_video_enable
		end

	is_events_enable: BOOLEAN assign set_is_events_enable
			-- Return true if the events functionnality is enabled.
		do
			Result := is_sub_system_enable ({GAME_SDL_EXTERNAL}.sdl_init_events)
		end

	is_haptic_enable: BOOLEAN assign set_is_haptic_enable
			-- Return true if the haptic (force feedback) functionnality is enabled.
		do
			Result := is_sub_system_enable ({GAME_SDL_EXTERNAL}.sdl_init_haptic)
		end

	is_joystick_enable: BOOLEAN assign set_is_joystick_enable
			-- Return true if the joystick functionnality is enabled.
		do
			Result := is_sub_system_enable ({GAME_SDL_EXTERNAL}.sdl_init_joystick)
		end

	is_video_enable: BOOLEAN assign set_is_video_enable
			-- Return true if the text surface functionnality is enabled.
		do
			Result := is_sub_system_enable ({GAME_SDL_EXTERNAL}.sdl_init_video)
		end

	set_is_events_enable (a_value: BOOLEAN)
			-- Assign to is_events_enable the value of a_value
		do
			if a_value then
				enable_events
			else
				disable_events
			end
		ensure
			is_assign: is_events_enable ~ a_value
		end

	set_is_haptic_enable (a_value: BOOLEAN)
			-- Assign to is_haptic_enable the value of a_value
		do
			if a_value then
				enable_haptic
			else
				disable_haptic
			end
		ensure
			is_assign: is_haptic_enable ~ a_value
		end

	set_is_joystick_enable (a_value: BOOLEAN)
			-- Assign to is_joystick_enable the value of a_value
		do
			if a_value then
				enable_joystick
			else
				disable_joystick
			end
		ensure
			is_assign: is_joystick_enable ~ a_value
		end

	set_is_video_enable (a_value: BOOLEAN)
			-- Assign to is_video_enable the value of a_value
		do
			if a_value then
				enable_video
			else
				disable_video
			end
		ensure
			is_assign: is_video_enable ~ a_value
		end
	
feature -- Touch devices

	refresh_touch_devices
			-- Update the touch_devices list. Note that
			-- all touch events will have to be reset
		do
			clear_touch_devices
			internal_touch_devices := Void
		end

	touch_device_count: INTEGER_32
			-- The number of touch device detected on the system.
			-- Note: Not necessary the same as touch_devices.count
			-- because a touch device can have been added or remove
			-- since the creation of the touch_devices list.
			-- In that case, used refresh_touch_devices to update
			-- the touch_devices (all touch events will have to be
			-- reset)
		do
			Result := {GAME_SDL_EXTERNAL}.sdl_getnumtouchdevices
		end

	touch_devices: CHAIN_INDEXABLE_ITERATOR [GAME_TOUCH_DEVICE]
			-- Every finger touch device detected in he system.
			-- This list does not update itself, you have to call
			-- refresh_touch_devices to update it (all touch
			-- events will have to be reset)
		local
			i: INTEGER_32
			l_touch_devices: CHAIN [GAME_TOUCH_DEVICE]
		do
			if attached internal_touch_devices as la_touch_devices then
				l_touch_devices := la_touch_devices
			else
				create {ARRAYED_LIST [GAME_TOUCH_DEVICE]} l_touch_devices.make (touch_device_count)
				from
					i := 1
				until
					i > touch_device_count
				loop
					l_touch_devices.extend (create {GAME_TOUCH_DEVICE}.make (i))
					i := i + 1
				end
				internal_touch_devices := l_touch_devices
			end
			create Result.make (l_touch_devices)
		end
	
feature {GAME_SDL_ANY} -- Touch devices implementation

	clear_touch_devices
			-- Clear every touch_devices events
		do
			if attached internal_touch_devices as la_devices then
				la_devices.do_all (agent {GAME_TOUCH_DEVICE}.clear_events)
			end
		end

	internal_touch_devices: detachable CHAIN [GAME_TOUCH_DEVICE]
			-- Internal values of the lazy evaluated touch_devices
	
feature -- Touch devices implementation

	Dollar_gesture_template_index: INTEGER_64 = -1
			-- The internal index of the managed touch device
	
feature -- Video methods

	displays: LIST [GAME_DISPLAY]
			-- All displays of the system. 0 display on error.
		require
			displays_is_video_enabled: is_video_enable
		local
			l_count, i: INTEGER_32
		do
			l_count := displays_count
			create {ARRAYED_LIST [GAME_DISPLAY]} Result.make (l_count)
			from
				i := 0
			until
				i >= l_count
			loop
				Result.extend (create {GAME_DISPLAY}.make (i))
				i := i + 1
			end
		end

	displays_count: INTEGER_32
			-- Return the number of display. 0 if error.
		require
			displays_count_is_video_enabled: is_video_enable
		do
			clear_error
			Result := {GAME_SDL_EXTERNAL}.sdl_getnumvideodisplays
			if Result < 0 then
				manage_error_code (Result, "An error occured while retriving the number of displays.")
				Result := 0
			end
		end

	renderer_drivers: LIST [GAME_RENDERER_DRIVER]
			-- All renderer driver of the system. 0 driver on error.
		require
			displays_is_video_enabled: is_video_enable
		local
			l_count, i: INTEGER_32
		do
			l_count := renderer_drivers_count
			create {ARRAYED_LIST [GAME_RENDERER_DRIVER]} Result.make (l_count)
			from
				i := 0
			until
				i >= l_count
			loop
				Result.extend (create {GAME_RENDERER_DRIVER}.make (i))
				i := i + 1
			end
		end

	renderer_drivers_count: INTEGER_32
			-- Return the number of renderer driver. 0 if error.
		require
			video_enabled: is_video_enable
		do
			clear_error
			Result := {GAME_SDL_EXTERNAL}.sdl_getnumrenderdrivers
			if Result < 0 then
				manage_error_code (Result, "An error occured while retriving the number of renderer drivers.")
				Result := 0
			end
		end

	windows: CHAIN_INDEXABLE_ITERATOR [GAME_WINDOW]
			-- Every GAME_WINDOW in the system.
		do
			create Result.make (internal_windows)
		end
	
invariant
	is_singleton: Instance_count.item = 1

		-- from ANY
	reflexive_equality: standard_is_equal (Current)
	reflexive_conformance: conforms_to (Current)

end -- class GAME_LIBRARY_CONTROLLER

Generated by ISE EiffelStudio