# 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=$1

	# Set package context for this function call.
	set_current_package "$package"

	local target_directory
	target_directory=$(path_libraries)

	# Include libraries listed using the content identifiers LIBS_xxx and LIBS0_xxx to LIBS9_xxx.
	local index content_identifier content_files
	for index in '' $(seq 0 9); do
		content_identifier="LIBS${index}_${package#PKG_}"
		content_files=$(content_files "$content_identifier")
		# Stop looping at the first unset files list.
		if [ -z "$content_files" ]; then
			return 0
		fi
		content_inclusion "$content_identifier" "$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=$1

	# Set package context for this function call.
	set_current_package "$package"

	local target_directory
	target_directory=$(path_fonts_ttf)

	# Include fonts listed using the content identifiers FONTS_xxx and FONTS0_xxx to FONTS9_xxx.
	local index content_identifier content_files
	for index in '' $(seq 0 9); do
		content_identifier="FONTS${index}_${package#PKG_}"
		content_files=$(content_files "$content_identifier")
		# Stop looping at the first unset files list.
		if [ -z "$content_files" ]; then
			return 0
		fi
		content_inclusion "$content_identifier" "$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=$1

	# WARNING: Support for huge files requires the ability to edit the value of PACKAGES_LIST,
	#          so this function should not implicitly spawn a subshell.

	local target_directory
	target_directory=$(
		# Context required because the game id can be package-specific,
		# when multiple games are provided by a single architecture.
		set_current_package "$package"
		path_game_data
	)

	# Include game files listed using the content identifiers GAME_xxx and GAME0_xxx to GAME9_xxx.
	local index content_identifier content_files
	for index in '' $(seq 0 9); do
		content_identifier="GAME${index}_${package#PKG_}"
		content_files=$(
			# Context required because the list of files can be package-specific,
			# with game engines using a distinct default list based on the package architecture.
			set_current_package "$package"
			content_files "$content_identifier"
		)
		# Stop looping at the first unset files list.
		if [ -z "$content_files" ]; then
			return 0
		fi
		content_inclusion "$content_identifier" "$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=$1

	# Set package context for this function call.
	set_current_package "$package"

	local target_directory
	target_directory=$(path_documentation)

	# Include documentation listed using the content identifiers DOC_xxx and DOC0_xxx to DOC9_xxx.
	local index content_identifier content_files
	for index in '' $(seq 0 9); do
		content_identifier="DOC${index}_${package#PKG_}"
		content_files=$(content_files "$content_identifier")
		# Stop looping at the first unset files list.
		if [ -z "$content_files" ]; then
			return 0
		fi
		content_inclusion "$content_identifier" "$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=$1 \
		package=$2 \
		target_path=$3

	# WARNING: Support for huge files requires the ability to edit the value of PACKAGES_LIST,
	#          so this function should not implicitly spawn a subshell.

	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 does not exist.
	local path_archive_contents
	path_archive_contents=$(path_archive_contents "$content_id")
	if [ ! -e "$path_archive_contents" ]; 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.
	(
		# Context required because the list of files can be package-specific,
		# with game engines using a distinct default list based on the package architecture.
		set_current_package "$package"
		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'
}

# Print the full list of paths to include, using a null byte as the separator.
# Warning: Because of the null bytes, this function output can not be stored in a variable.
# USAGE: content_inclusion_list_paths $content_identifier
content_inclusion_list_paths() {
	local content_identifier
	content_identifier="$1"

	local path_archive_contents
	path_archive_contents=$(path_archive_contents "$content_identifier")
	content_inclusion_find_options "$content_identifier" |
		env --chdir "$path_archive_contents" xargs --null find |
		env --ignore-environment sort --zero-terminated
}

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

	local path_archive_contents paths_list_base64
	path_archive_contents=$(path_archive_contents "$content_identifier")
	## 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)
	mkdir --parents "$destination_path"
	(
		## Changing the current directory is required for correct globs expansion.
		cd "$path_archive_contents"
		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
	)
}

