# Fetch icon files, convert them to the expected format, include them in the given package.
#
# This function is the one that should be called from game scripts,
# it can take several applications as its arguments,
# and default to handle all applications if none are explicitely given.
#
# USAGE: content_inclusion_icons [$package [$application…]]
content_inclusion_icons() {
	# Do nothing if icons inclusion has been disabled.
	local option_icons
	option_icons=$(option_value 'icons')
	if [ "$option_icons" -eq 0 ]; then
		return 0
	fi

	# Ensure that the commands required for icons extraction are available
	requirements_check_icons

	# Get the package that should include the icons.
	local package
	if [ $# -ge 1 ]; then
		package="$1"
		shift 1
	else
		package=$(current_package)
	fi

	# If an optional icons archive has been provided, use that instead of the icons shipped with the game
	if archive_is_available 'ARCHIVE_ICONS'; then
		information_icons_inclusion
		content_inclusion_optional_icons_archive "$package"
		## Return early if an optional icons archive has been used.
		return 0
	fi

	# If no applications are explicitely listed,
	# try to fetch the icons for all applications.
	if [ "$#" -eq 0 ]; then
		applications_list=$(applications_list)
		## If content_inclusion_icons has been called with no argument, the applications list should not be empty.
		if [ -z "$applications_list" ]; then
			error_applications_list_empty
			return 1
		fi
		content_inclusion_icons "$package" $applications_list
		return 0
	fi

	information_icons_inclusion

	local application
	for application in "$@"; do
		icons_inclusion_single_application "$package" "$application"
	done
}

# Fetch files from the archive, and include them into the package skeleton.
# A list of default content identifiers is used.
# USAGE: content_inclusion_default
content_inclusion_default() {
	information_content_inclusion

	local packages_list
	packages_list=$(packages_list)

	local package unity3d_plugins
	for package in $packages_list; do
		## TODO: content_inclusion_unity3d_plugins should always be called with Unity3D games.
		##       If UNITY3D_PLUGINS is empty no plugin should be included, and the Plugins directory deleted.
		unity3d_plugins=$(unity3d_plugins)
		if [ -n "$unity3d_plugins" ]; then
			content_inclusion_unity3d_plugins "$package"
		fi
		content_inclusion_default_libraries "$package"
		content_inclusion_default_fonts "$package"
		content_inclusion_default_game_data "$package"
		content_inclusion_default_documentation "$package"
	done

	# Including files used to apply tweaks to the WINE prefix should only be done in the packages including the game binaries.
	local package_architecture
	for package in $packages_list; do
		package_architecture=$(package_architecture "$package")
		case "$package_architecture" in
			('64'|'32')
				content_inclusion_wineprefix_tweaks "$package"
			;;
		esac
	done

	# content_inclusion_extra_libraries must be called in its own loop, at the end of the content inclusion process.
	# Since it can trigger an archive_extraction call leading to a set_standard_permissions call,
	# calling it earlier could lead to unwanted permissions reset.
	for package in $packages_list; do
		content_inclusion_extra_libraries "$package"
	done

	# Delete the remaining files extracted from archives but not included in any package
	## Skip the automatic clean-up for game scripts targeting ./play.it ≤ 2.25.
	if ! compatibility_level_is_at_least '2.26'; then
		return 0
	fi
	rm --force --recursive "${PLAYIT_WORKDIR}/gamedata"
}

# Fetch files from the archive, and include them into the package skeleton.
# A list of default content identifiers is used, limited to native libraries for a single package.
# USAGE: content_inclusion_default_libraries $package
content_inclusion_default_libraries() {
	local package
	package="$1"

	local package_suffix files_list
	package_suffix="${package#PKG_}"
	files_list=$(context_name "CONTENT_LIBS_${package_suffix}_FILES")
	if [ -z "$files_list" ]; then
		## Check if a default files list is set for the current engine,
		## return early otherwise.
		local content_files
		content_files=$(content_files "LIBS_${package_suffix}")
		if [ -z "$content_files" ]; then
			return 0
		fi
	fi

	local target_directory
	## On Arch Linux and Gentoo the libraries path can change based on the package architecture.
	target_directory=$(
		set_current_package "$package"
		path_libraries
	)
	content_inclusion "LIBS_${package_suffix}" "$package" "$target_directory"
	local index
	for index in $(seq 0 9); do
		files_list=$(context_name "CONTENT_LIBS${index}_${package_suffix}_FILES")
		if [ -z "$files_list" ]; then
			## Stop looping at the first unset files list.
			return 0
		fi
		content_inclusion "LIBS${index}_${package_suffix}" "$package" "$target_directory"
	done
}

# Fetch files from the archive, and include them into the package skeleton.
# A list of default content identifiers is used, limited to TTF fonts for a single package.
# USAGE: content_inclusion_default_fonts $package
content_inclusion_default_fonts() {
	local package
	package="$1"

	local package_suffix files_list
	package_suffix="${package#PKG_}"
	files_list=$(context_name "CONTENT_FONTS_${package_suffix}_FILES")
	if [ -z "$files_list" ]; then
		## Check if a default files list is set for the current engine,
		## return early otherwise.
		local content_files
		content_files=$(content_files "FONTS_${package_suffix}")
		if [ -z "$content_files" ]; then
			return 0
		fi
	fi

	local target_directory
	target_directory=$(
		set_current_package "$package"
		path_fonts_ttf
	)
	content_inclusion "FONTS_${package_suffix}" "$package" "$target_directory"
	local index
	for index in $(seq 0 9); do
		files_list=$(context_name "CONTENT_FONTS${index}_${package_suffix}_FILES")
		if [ -z "$files_list" ]; then
			## Stop looping at the first unset files list.
			return 0
		fi
		content_inclusion "FONTS${index}_${package_suffix}" "$package" "$target_directory"
	done
}

# Fetch files from the archive, and include them into the package skeleton.
# A list of default content identifiers is used, limited to game data files for a single package.
# USAGE: content_inclusion_default_game_data $package
content_inclusion_default_game_data() {
	local package
	package="$1"

	local package_suffix files_list
	package_suffix="${package#PKG_}"
	files_list=$(context_name "CONTENT_GAME_${package_suffix}_FILES")
	if [ -z "$files_list" ]; then
		## Check if a default files list is set for the current engine,
		## return early otherwise.
		local content_files
		content_files=$(content_files "GAME_${package_suffix}")
		if [ -z "$content_files" ]; then
			return 0
		fi
	fi

	local target_directory
	target_directory=$(
		set_current_package "$package"
		path_game_data
	)
	content_inclusion "GAME_${package_suffix}" "$package" "$target_directory"
	local index
	for index in $(seq 0 9); do
		files_list=$(context_name "CONTENT_GAME${index}_${package_suffix}_FILES")
		if [ -z "$files_list" ]; then
			## Stop looping at the first unset files list.
			return 0
		fi
		content_inclusion "GAME${index}_${package_suffix}" "$package" "$target_directory"
	done

}

# Fetch files from the archive, and include them into the package skeleton.
# A list of default content identifiers is used, limited to documentation files for a single package.
# USAGE: content_inclusion_default_documentation $package
content_inclusion_default_documentation() {
	local package
	package="$1"

	local package_suffix files_list
	package_suffix="${package#PKG_}"
	files_list=$(context_name "CONTENT_DOC_${package_suffix}_FILES")
	if [ -z "$files_list" ]; then
		## Check if a default files list is set for the current engine,
		## return early otherwise.
		local content_files
		content_files=$(content_files "DOC_${package_suffix}")
		if [ -z "$content_files" ]; then
			return 0
		fi
	fi

	local target_directory
	target_directory=$(
		set_current_package "$package"
		path_documentation
	)
	content_inclusion "DOC_${package_suffix}" "$package" "$target_directory"
	local index
	for index in $(seq 0 9); do
		files_list=$(context_name "CONTENT_DOC${index}_${package_suffix}_FILES")
		if [ -z "$files_list" ]; then
			## Stop looping at the first unset files list.
			return 0
		fi
		content_inclusion "DOC${index}_${package_suffix}" "$package" "$target_directory"
	done
}

# Fetch files from the archive, and include them into the package skeleton.
# USAGE: content_inclusion $content_id $package $target_path
content_inclusion() {
	local content_id package target_path
	content_id="$1"
	package="$2"
	target_path="$3"

	information_content_inclusion

	# Check that the given package is valid
	if ! package_is_included_in_packages_list "$package"; then
		error_current_package_not_in_list "$package"
		return 1
	fi

	# Return early if the content source path is not set.
	local content_path
	## content_path relies on the package context being set, through a call to content_path_default.
	content_path=$(
		set_current_package "$package"
		content_path "$content_id"
	)
	if [ -z "$content_path" ]; then
		return 0
	fi
	# Return early if the content source path does not exist.
	content_path_full="${PLAYIT_WORKDIR}/gamedata/${content_path}"
	if [ ! -e "$content_path_full" ]; then
		return 0
	fi

	# Debian - Handle huge files by splitting them in 9GB chunks,
	# and include the chunks in dedicated packages.
	if [ "$content_id" = "GAME_${package#PKG_}" ]; then
		local option_package
		option_package=$(option_value 'package')
		if [ "$option_package" = 'deb' ]; then
			local huge_files
			huge_files=$(huge_files_list "$package")
			if [ -n "$huge_files" ]; then
				content_inclusion_chunks "$package"
			fi
		fi
	fi

	# Set path to destination,
	# ensuring it is an absolute path.
	local package_path destination_path
	package_path=$(package_path "$package")
	destination_path=$(realpath --canonicalize-missing "${package_path}${target_path}")

	# Proceed with the actual files inclusion
	content_inclusion_include_paths "$content_id" "$destination_path"
}

# Convert a list of patterns to include into a series of find options
# USAGE: content_inclusion_find_options $content_identifier
# RETURN: a full find options string, using null-byte as separator
content_inclusion_find_options() {
	local content_identifier
	content_identifier="$1"

	local content_patterns_list
	content_patterns_list=$(content_files "$content_identifier")

	local content_pattern first_path_shown printf_format
	first_path_shown=0
	printf '.\0(\0'
	while read -r content_pattern; do
		## Skip empty lines.
		if [ -z "$content_pattern" ]; then
			continue
		fi
		if [ $first_path_shown -eq 0 ]; then
			printf_format='-ipath\0./%s'
			first_path_shown=1
		else
			printf_format='\0-o\0-ipath\0./%s'
		fi
		## Silence ShellCheck false-positive
		## Don't use variables in the printf format string. Use printf "..%s.." "$foo".
		# shellcheck disable=SC2059
		printf -- "$printf_format" "${content_pattern#./}"
	done <<- EOL
	$(printf '%s' "$content_patterns_list")
	EOL
	printf '\0)\0-print0'
}

# Display the full list of paths to include, using a null-byte as the separator
# USAGE: content_inclusion_list_paths $content_identifier
# RETURN: a sorted list of paths, separated by null-bytes
content_inclusion_list_paths() {
	local content_identifier
	content_identifier="$1"

	local content_path content_path_full
	content_path=$(content_path "$content_identifier")
	content_path_full="${PLAYIT_WORKDIR}/gamedata/${content_path}"
	## TODO: env --chdir could be used instead of cd in a subshell.
	(
		cd "$content_path_full"
		content_inclusion_find_options "$content_identifier" | xargs --null find
	) | env --ignore-environment sort --zero-terminated
}

# Fetch a list of paths from the archives contents
# USAGE: content_inclusion_include_paths $content_identifier $destination_path
content_inclusion_include_paths() {
	local content_identifier destination_path
	content_identifier="$1"
	destination_path="$2"

	local content_path content_path_full paths_list_base64
	content_path=$(content_path "$content_identifier")
	content_path_full="${PLAYIT_WORKDIR}/gamedata/${content_path}"
	mkdir --parents "$destination_path"
	## TODO: env --chdir could be used instead of cd in a subshell.
	(
		cd "$content_path_full"
		## The list of paths is encoded in base64, because it uses null bytes as a separator and null bytes can not be stored in a variable.
		paths_list_base64=$(content_inclusion_list_paths "$content_identifier" | base64 --wrap=0)
		printf '%s' "$paths_list_base64" | base64 --decode | xargs --null --no-run-if-empty cp \
			--force \
			--link \
			--recursive \
			--no-dereference \
			--parents \
			--preserve=links \
			--target-directory "$destination_path"
		## Delete paths that have already been copied, so they will not end up duplicated in the packages.
		printf '%s' "$paths_list_base64" | base64 --decode | xargs --null --no-run-if-empty rm \
			--force \
			--recursive
	)
}

