# List the application identifiers for the current game or expansion
# USAGE: applications_list
# RETURN: a list of application identifiers, one per line,
#         or an empty string if there is no application (common case for expansions)
applications_list() {
	# Fetch the explicit list if it is set
	local applications_list
	applications_list=$(context_value 'APPLICATIONS_LIST')

	# Parse the environment to compute an applications list from it
	## TODO: This only has support for applications using an identifier like APP_xxx
	##       where xxx can not include an underscore. Underscores should be allowed.
	if [ -z "$applications_list" ]; then
		local sed_expression
		# The following expression matches:
		# - APP_xxx_EXE
		# - APP_xxx_SCUMMID
		# - APP_xxx_TYPE
		# and the suffixed variants of these variables.
		sed_expression='s/^\(APP_[0-9A-Z]\+\)_\(EXE\|SCUMMID\|TYPE\)\(_[0-9A-Z]\+\)*=.*/\1/p'
		applications_list=$(set | sed --silent --expression="$sed_expression")
	fi

	# Fall back on the default applications list for the current game engine
	if [ -z "$applications_list" ]; then
		local game_engine
		game_engine=$(game_engine)
		case "$game_engine" in
			('ags')
				applications_list='APP_MAIN'
			;;
			('gamemaker')
				applications_list=$(applications_list_gamemaker)
			;;
			('unity3d')
				# Unity3D games are expected to provide a single application
				applications_list='APP_MAIN'
			;;
			('visionaire')
				applications_list=$(visionaire_applications_list)
			;;
		esac
	fi

	printf '%s\n' $applications_list | list_clean
}

# Print the type of prefix to use for the given application.
# If no type is explicitely set from the game script, it defaults to "symlinks".
# The supported prefix types are:
# - "symlinks", the default, generate our usual symbolic links farm
# - "none", no prefix is generated, the game is run from the read-only system directory
# USAGE: application_prefix_type $application
# RETURN: the prefix type keyword, from the supported values
application_prefix_type() {
	# Prefix types:
	# - "symlinks", the default, generate our usual symbolic links farm
	# - "none", no prefix is generated, the game is run from the read-only system directory

	local application
	application="$1"

	# Set the prefix type for the current application.
	local prefix_type
	prefix_type=$(context_value "${application}_PREFIX_TYPE")

	# Fall back on the default prefix type for the current game.
	if [ -z "$prefix_type" ]; then
		prefix_type=$(context_value 'APPLICATIONS_PREFIX_TYPE')
	fi

	# Fall back on the default prefix type for the current application type.
	if [ -z "$prefix_type" ]; then
		local application_type
		application_type=$(application_type "$application")
		case "$application_type" in
			('renpy')
				prefix_type='none'
			;;
			('scummvm')
				prefix_type='none'
			;;
			(*)
				prefix_type='symlinks'
			;;
		esac
	fi

	# Check that a supported prefix type has been set.
	case "$prefix_type" in
		('symlinks'|'none')
			## This is a supported type, no error to throw.
		;;
		(*)
			error_unknown_prefix_type "$prefix_type"
			return 1
		;;
	esac

	printf '%s' "$prefix_type"
}

# print the id of the given application
# USAGE: application_id $application
# RETURN: the application id, limited to the characters set [-_0-9a-z]
#         the id can not start nor end with a character from the set [-_]
application_id() {
	local application
	application="$1"

	# Get the application type from its identifier
	# Fall back on the game id if no value is set
	local application_id
	application_id=$(context_value "${application}_ID")
	if [ -z "$application_id" ]; then
		application_id=$(game_id)
	fi

	# Check that the id fits the format restrictions
	if ! printf '%s' "$application_id" |
		grep --quiet --regexp='^[0-9a-z][-_0-9a-z]\+[0-9a-z]$'
	then
		error_application_id_invalid "$application" "$application_id"
		return 1
	fi

	printf '%s' "$application_id"
}

# Print the name of the binary targeted by the given application
# USAGE: application_exe $application
# RETURN: the binary file name,
#         or an empty string is none is set
application_exe() {
	local application
	application="$1"

	local application_exe
	application_exe=$(context_value "${application}_EXE")

	# If no binary is explicitly set, fall back on the default value for the current game engine.
	if [ -z "$application_exe" ]; then
		local game_engine
		game_engine=$(game_engine)
		## TODO: A default value could be set for Unreal Engine 4 games.
		##       Based on that default value, the fallback wrestool options for these games might need updating.
		case "$game_engine" in
			('gamemaker')
				application_exe=$(application_exe_gamemaker)
			;;
			('unity3d')
				application_exe=$(unity3d_application_exe_default "$application")
			;;
			('visionaire')
				application_exe=$(visionaire_application_exe)
			;;
		esac
	fi

	printf '%s' "$application_exe"
}

# Print the full path to the application binary.
# An empty string is printed if the binary could not be found, without throwing an error.
# USAGE: application_exe_path $application_exe
application_exe_path() {
	local application_exe
	application_exe="$1"

	# Look for the application binary in the current package.
	local package package_path path_game_data application_exe_path
	package=$(current_package)
	package_path=$(package_path "$package")
	path_game_data=$(path_game_data)
	application_exe_path="${package_path}${path_game_data}/${application_exe}"
	if [ -f "$application_exe_path" ]; then
		printf '%s' "$application_exe_path"
		return 0
	fi

	# Look for the application binary in all packages.
	local packages_list
	packages_list=$(packages_list)
	for package in $packages_list; do
		package_path=$(package_path "$package")
		path_game_data=$(
			set_current_package "$package"
			path_game_data
		)
		application_exe_path="${package_path}${path_game_data}/${application_exe}"
		if [ -f "$application_exe_path" ]; then
			printf '%s' "$application_exe_path"
			return 0
		fi
	done

	# Look for the application binary in the temporary path for archive content.
	## Reaching this step with CONTENT_PATH_DEFAULT unset triggers an error.
	local content_path_default
	content_path_default=$(content_path_default)
	if [ -n "$content_path_default" ]; then
		local application_exe_path
		application_exe_path="${PLAYIT_WORKDIR}/gamedata/${content_path_default}/${application_exe}"
		if [ -f "$application_exe_path" ]; then
			printf '%s' "$application_exe_path"
			return 0
		fi
	fi
}

# print the name of the given application, for display in menus
# USAGE: application_name $application
# RETURN: the pretty version of the application name
application_name() {
	local application
	application="$1"

	# Get the application name from its identifier
	# Fall back on the game name if no value is set
	local application_name
	application_name=$(context_value "${application}_NAME")
	if [ -z "$application_name" ]; then
		application_name=$(game_name)
	fi

	printf '%s' "$application_name"
}

# Exits 1 if the give category is not an OpenDesktop Main Category as per
# https://specifications.freedesktop.org/menu-spec/1.0/category-registry.html
# USAGE: is_valid_category $category
is_valid_category() {
	local category
	category="$1"

	case "$category" in
		'AudioVideo') ;;
		'Audio') ;;
		'Video') ;;
		'Development') ;;
		'Education') ;;
		'Game') ;;
		'Graphics') ;;
		'Network') ;;
		'Office') ;;
		'Settings') ;;
		'System') ;;
		'Utility') ;;
		*) return 1 ;;
	esac
	return 0
}

# Print the menu category that should be used for the current application.
# USAGE: application_category $application
application_category() {
	local application
	application="$1"

	# Get the application category from its identifier
	local application_category
	application_category=$(get_value "${application}_CAT")
	## If no category is explicitely set, fall back on "Game"
	if [ -z "$application_category" ]; then
		application_category='Game'
	fi

	# Check the category against XDG specification.
	if ! is_valid_category "$application_category"; then
		warning_application_category_unspecified "$application" "$application_category"
	fi

	printf '%s' "$application_category"
}

# Print the pre-run actions for the given application.
# USAGE: application_prerun $application
# RETURN: the pre-run actions, can span over multiple lines,
#         or an empty string if there are none
application_prerun() {
	local application
	application="$1"

	local application_prerun
	application_prerun=$(context_value "${application}_PRERUN")

	# Run engine specific actions
	local game_engine
	game_engine=$(game_engine)
	case "$game_engine" in
		('gamemaker')
			application_prerun="$(application_prerun_gamemaker)
			$application_prerun"
		;;
		('unity3d')
			## Use a dedicated log file for the current game session.
			application_prerun="$application_prerun
			mkdir --parents logs"
		;;
	esac

	# WARNING: This relies on a system deprecated since ./play.it 2.33.0,
	#          LD_PRELOAD_SOURCE should be used instead.
	# If LD_PRELOAD hacks are provided in the current package, include them in the pre-run actions.
	local package hacks_list
	package=$(current_package)
	hacks_list=$(hacks_included_in_package "$package")
	if [ -n "$hacks_list" ]; then
		local hack hack_prerun
		for hack in $hacks_list; do
			hack_prerun=$(hack_application_prerun "$hack")
			application_prerun="$application_prerun
$hack_prerun"
		done
	fi

	# Ensure the pre-run actions string always end with a line break.
	printf '%s\n' "$application_prerun"
}

# Print the post-run actions for the given application.
# USAGE: application_postrun $application
# RETURN: the post-run actions, can span over multiple lines,
#         or an empty string if there are none
application_postrun() {
	local application
	application="$1"

	local application_postrun
	application_postrun=$(context_value "${application}_POSTRUN")

	# Ensure the post-run actions string always end with a line break.
	printf '%s\n' "$application_postrun"
}

# print the options string for the given application
# USAGE: application_options $application
# RETURN: the options string on a single line,
#         or an empty string if no options are set
application_options() {
	# Get the application options string from its identifier
	local application application_options
	application="$1"
	application_options=$(context_value "${application}_OPTIONS")

	# Add engine specific options
	local game_engine
	game_engine=$(game_engine)
	case "$game_engine" in
		('unity3d')
			## Use a dedicated log file for the current game session.
			## The quotes are not truly required, but they make ShellCheck happy.
			application_options="$application_options -logFile \"./logs/\$(date +%F-%R).log\""
		;;
	esac

	# Check that the options string does not span multiple lines
	local line_breaks_number
	line_breaks_number=$(printf '%s' "$application_options" | wc --lines)
	if [ "$line_breaks_number" -gt 0 ]; then
		error_variable_multiline "${application}_OPTIONS"
		return 1
	fi

	printf '%s' "$application_options"
}

