# Generate packages from the given list
# USAGE: packages_generation $package[…]
packages_generation() {
	# If not explicit packages list is given, generate all packages
	if [ $# -eq 0 ]; then
		local packages_list
		packages_list=$(packages_list)
		packages_generation $packages_list
		return 0
	fi

	information_packages_generation

	local option_package
	option_package=$(option_value 'package')
	case "$option_package" in
		('arch')
			archlinux_packages_metadata "$@"
			archlinux_packages_build "$@"
		;;
		('deb')
			debian_packages_metadata "$@"
			debian_packages_build "$@"
		;;
		('gentoo')
			gentoo_packages_metadata "$@"
			gentoo_packages_build "$@"
		;;
		('egentoo')
			egentoo_packages_metadata "$@"
			egentoo_packages_build "$@"
		;;
	esac
}

# Print the full list of packages that should be built from the current archive
# If no value is set to PACKAGES_LIST or some archive-specific variant of PACKAGES_LIST,
# the following default value is returned: "PKG_MAIN".
# USAGE: packages_list
# RETURN: a list of package identifiers,
#         separated by line breaks
packages_list() {
	local packages_list
	packages_list=$(context_value 'PACKAGES_LIST')

	# Fall back on the default packages list for the current game engine
	if [ -z "$packages_list" ]; then
		local game_engine
		game_engine=$(game_engine)
		case "$game_engine" in
			('visionaire')
				packages_list=$(visionaire_packages_list)
			;;
		esac
	fi

	# Fall back of the default packages list (a single package identified by "PKG_MAIN")
	if [ -z "$packages_list" ]; then
		packages_list='PKG_MAIN'
	fi

	local package
	for package in $packages_list; do
		printf '%s\n' "$package"
	done
}

# Check if the given package is included in the list of packages that should be built
# USAGE: package_is_included_in_packages_list $package
# RETURN: 0 if the package is included, 1 if it is not
package_is_included_in_packages_list() {
	local package
	package="$1"

	local packages_list
	packages_list=$(packages_list)

	printf '%s' "$packages_list" |
		grep --quiet --fixed-strings --word-regexp "$package"
}

# Print the list of the packages that would be generated from the given archive.
# USAGE: packages_print_list $archive
# RETURN: a list of package file names, one per line
packages_print_list() {
	local archive
	archive="$1"

	local option_package PLAYIT_CONTEXT_ARCHIVE
	option_package=$(option_value 'package')
	set_current_archive "$archive"
	case "$option_package" in
		('egentoo')
			local package_name
			package_name=$(egentoo_package_name)
			printf '%s\n' "$package_name"
		;;
		(*)
			local packages_list package package_name
			packages_list=$(packages_list)
			for package in $packages_list; do
				package_name=$(package_name "$package")
				printf '%s\n' "$package_name"
			done
		;;
	esac
}

# Print the id of the given package
# USAGE: package_id $package
# RETURNS: the package id, as a non-empty string
package_id() {
	local package
	package="$1"

	local package_id
	package_id=$(context_value "${package}_ID")

	# Fall back on the default package id for the current game engine
	if [ -z "$package_id" ]; then
		local game_engine
		game_engine=$(game_engine)
		case "$game_engine" in
			('visionaire')
				package_id=$(visionaire_package_id "$package")
			;;
		esac
	fi

	# Fall back on the game id if no package id is explicitly set
	if [ -z "$package_id" ]; then
		## We need to explicitly set the context here,
		## because the value of GAME_ID might be specific to the current package.
		package_id=$(
			set_current_package "$package"
			game_id
		)
		## Include the expansion id if one is available.
		local expansion_id
		expansion_id=$(expansion_id)
		if [ -n "$expansion_id" ]; then
			package_id="${package_id}-${expansion_id}"
		fi
	fi

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

	# Apply tweaks specific to the target package format.
	local option_package
	option_package=$(option_value 'package')
	case "$option_package" in
		('arch')
			package_id=$(archlinux_package_id "$package_id")
		;;
		('gentoo'|'egentoo')
			package_id=$(gentoo_package_id "$package_id")
		;;
	esac

	printf '%s' "$package_id"
}

# Print the architecture of the given package
# USAGE: package_architecture $package
# RETURNS: the package architecture, as one of the following values:
#          - 32
#          - 64
#          - all
package_architecture() {
	local package
	package="$1"

	local package_architecture
	package_architecture=$(context_value "${package}_ARCH")

	# If no architecture is explictly set for the given package, fall back to "all".
	if [ -z "$package_architecture" ]; then
		package_architecture='all'
	fi

	printf '%s' "$package_architecture"
}

# Print the desciption of the given package
# USAGE: package_description $package
# RETURNS: the package description, a non-empty string that should not include line breaks
package_description() {
	local package
	package="$1"

	local package_description
	package_description=$(context_value "${package}_DESCRIPTION")

	# Fall back on the default package description for the current game engine
	if [ -z "$package_description" ]; then
		local game_engine
		game_engine=$(game_engine)
		case "$game_engine" in
			('visionaire')
				package_description=$(visionaire_package_description "$package")
			;;
		esac
	fi

	# Check that the package description does not span multiple lines
	if [ "$(printf '%s' "$package_description" | wc --lines)" -gt 0 ]; then
		error_variable_multiline "${package}_DESCRIPTION"
		return 1
	fi

	printf '%s' "$package_description"
}

# Print the file name of the given package
# USAGE: package_name $package
# RETURNS: the file name, as a string
package_name() {
	local package
	package="$1"

	local option_package package_name
	option_package=$(option_value 'package')
	case "$option_package" in
		('arch')
			package_name=$(package_name_archlinux "$package")
		;;
		('deb')
			package_name=$(package_name_debian "$package")
		;;
		('gentoo')
			package_name=$(package_name_gentoo "$package")
		;;
		('egentoo')
			package_name=$(egentoo_package_name)
		;;
	esac

	printf '%s' "$package_name"
}

# Get the path to the directory where the given package is prepared.
# USAGE: package_path $package
# RETURNS: path to a directory, it is not checked that it exists or is writable
package_path() {
	local package
	package="$1"

	local option_package package_name package_path
	option_package=$(option_value 'package')
	case "$option_package" in
		('arch')
			package_path=$(package_path_archlinux "$package")
		;;
		('deb')
			package_path=$(package_path_debian "$package")
		;;
		('gentoo')
			package_path=$(package_path_gentoo "$package")
		;;
		('egentoo')
			package_path=$(package_path_egentoo "$package")
		;;
	esac

	printf '%s/packages/%s' "$PLAYIT_WORKDIR" "$package_path"
}

# Print the maintainer string
# USAGE: package_maintainer
# RETURNS: the package maintainer, as a non-empty string
package_maintainer() {
	local maintainer
	maintainer=''

	# Try to get a maintainer string from environment variables used by Debian tools.
	if ! variable_is_empty 'DEBEMAIL'; then
		if ! variable_is_empty 'DEBFULLNAME'; then
			maintainer="$DEBFULLNAME <${DEBEMAIL}>"
		else
			maintainer="$DEBEMAIL"
		fi
	fi
	if [ -n "$maintainer" ]; then
		printf '%s' "$maintainer"
		return 0
	fi

	# Try to get a maintainer string from /etc/makepkg.conf.
	if
		[ -r '/etc/makepkg.conf' ] &&
		grep --quiet '^PACKAGER=' '/etc/makepkg.conf'
	then
		if grep --quiet '^PACKAGER=".*"' '/etc/makepkg.conf'; then
			maintainer=$(sed --silent 's/^PACKAGER="\(.*\)"/\1/p' '/etc/makepkg.conf')
		elif grep --quiet "^PACKAGER='.*'" '/etc/makepkg.conf'; then
			maintainer=$(sed --silent "s/^PACKAGER='\\(.*\\)'/\\1/p" '/etc/makepkg.conf')
		else
			maintainer=$(sed --silent 's/^PACKAGER=\(.*\)/\1/p' '/etc/makepkg.conf')
		fi
	fi
	if [ -n "$maintainer" ]; then
		printf '%s' "$maintainer"
		return 0
	fi

	# Compute a maintainer e-mail from the current hostname and user name,
	# falling back to "user@localhost".
	local hostname
	if command -v 'hostname' >/dev/null 2>&1; then
		hostname=$(hostname)
	elif [ -r '/etc/hostname' ]; then
		hostname=$(cat '/etc/hostname')
	else
		hostname='localhost'
	fi
	local username
	if ! variable_is_empty 'USER'; then
		username="$USER"
	elif command -v 'whoami' >/dev/null 2>&1; then
		username=$(whoami)
	elif command -v 'id' >/dev/null 2>&1; then
		username=$(id --name --user)
	else
		username='user'
	fi
	printf '%s@%s' "$username" "$hostname"
}

# Print the package version string
# USAGE: package_version
# RETURNS: the package version, as a non-empty string
package_version() {
	# Get the version string for the current archive.
	local script_version_string archive package_version
	script_version_string=$(script_version)
	archive=$(current_archive)
	package_version=$(get_value "${archive}_VERSION")
	## Fall back on "1.0-1" if no version string is explicitly set.
	if [ -z "$package_version" ]; then
		package_version='1.0-1'
	fi
	package_version="${package_version}+${script_version_string}"

	# Portage does not like some of our version names
	# cf. https://devmanual.gentoo.org/ebuild-writing/file-format/index.html
	local option_package
	option_package=$(option_value 'package')
	case "$option_package" in
		('gentoo'|'egentoo')
			package_version=$(gentoo_package_version "$package_version")
		;;
	esac

	printf '%s' "$package_version"
}

# Print the list of package names provided by the given package
# This list is used to ensure conflicting packages can not be installed at the same time.
# USAGE: package_provides $package
# RETURN: a list of provided package names,
#         one per line,
#         or an empty string
package_provides() {
	local package
	package="$1"

	local package_provides
	package_provides=$(context_value "${package}_PROVIDES")

	# Return early if there is no package name to print
	if [ -z "$package_provides" ]; then
		return 0
	fi

	printf '%s' "$package_provides" | list_clean
}

# Print the actions that should be run post-installation for the given package
# USAGE: package_postinst_actions $package
# RETURN: a list of actions, that can span over several lines,
#         the list can be empty
package_postinst_actions() {
	local package
	package="$1"

	local postinst_actions
	postinst_actions=$(get_value "${package}_POSTINST_RUN")

	# Return early if no action is set.
	if [ -z "$postinst_actions" ]; then
		return 0
	fi

	# Ensure the list of actions always end with a line break.
	printf '%s\n' "$postinst_actions"
}

# Print the actions that should be run pre-removal for the given package
# USAGE: package_prerm_actions $package
# RETURN: a list of actions, that can span over several lines,
#         the list can be empty
package_prerm_actions() {
	local package
	package="$1"

	local prerm_actions
	prerm_actions=$(get_value "${package}_PRERM_RUN")

	# Return early if no action is set.
	if [ -z "$prerm_actions" ]; then
		return 0
	fi

	# Ensure the list of actions always end with a line break.
	printf '%s\n' "$prerm_actions"
}

# Print the warning messages that should be displayed at the end of the given package installation
# USAGE: package_postinst_warnings $package
# RETURN: one or several messages, separated by line breaks,
#         the message can be empty
package_postinst_warnings() {
	local package
	package="$1"

	get_value "${package}_POSTINST_WARNINGS"
}

