# Get the path to search for archives
# USAGE: archives_path_base
# RETURN: a string representing a path
archives_path_base() {
	# Try to get a path from the $PLAYIT_ARCHIVES_PATH_BASE environment variable,
	# if it is not set $PWD is used as a fallback.
	printf '%s' "${PLAYIT_ARCHIVES_PATH_BASE:-$PWD}"
}

# Set up the base archive
# USAGE: archive_initialize_base
# RETURN: 0 if the archive is found,
#         1 if it is missing
archive_initialize_base() {
	local archive_identifier
	archive_identifier='SOURCE_ARCHIVE'

	# A file path might have already been set, if one has been given on the command line.
	# If this is the case, we only want to use an archive that uses the same name.
	## The output redirection must be done inside the subshell, or bash --posix will ignore it.
	archive_name_expected=$(archive_name "$archive_identifier" 2>/dev/null) || true

	local archives_list archive_candidate
	archives_list=$(archives_return_list)
	for archive_candidate in $archives_list; do
		archive_path=$(archive_path "$archive_candidate")
		if [ -f "$archive_path" ]; then
			local archive_name
			archive_name=$(archive_name "$archive_candidate")

			# Skip candidate archives that do not have the expected name.
			if \
				[ -n "$archive_name_expected" ] && \
				[ "$archive_name" != "$archive_name_expected" ]
			then
				continue
			fi

			export "${archive_identifier}_NAME=${archive_name}"
			export "${archive_identifier}_PATH=${archive_path}"
			## Cache the path to the candidate archive, to prevent the need to re-compute it later.
			export "${archive_candidate}_PATH=${archive_path}"
			## Old game scripts relying on `archive_extraction 'SOURCE_ARCHIVE'` instead of `archive_extraction_default` expect either SOURCE_ARCHIVE_TYPE or SOURCE_ARCHIVE_EXTRACTOR to be set.
			local archive_extractor archive_extractor_options archive_type
			archive_extractor=$(archive_extractor "$archive_candidate")
			archive_extractor_options=$(archive_extractor_options "$archive_candidate")
			archive_type=$(archive_type "$archive_candidate")
			export "${archive_identifier}_EXTRACTOR=${archive_extractor}"
			export "${archive_identifier}_EXTRACTOR_OPTIONS=${archive_extractor_options}"
			export "${archive_identifier}_TYPE=${archive_type}"
			## Export the context archive
			set_current_archive "$archive_candidate"
			## Some old game scripts might expect the variable $SOURCE_ARCHIVE to be set
			export SOURCE_ARCHIVE="$archive_path"
			## Look for extra parts
			archive_initialize_extra_parts "$archive_candidate"
			## Update the list of archives that are going to be used
			archives_used_add "$archive_candidate"
			## Archives integrity can not yet be checked, because $PLAYIT_WORKDIR is not set yet,
			## and it is required to cache computed hashes. archives_integrity_check must be called later.
			return 0
		fi
	done

	# Throw an error if no archive candidate has been found
	error_archive_not_found $archives_list
	return 1
}

# Set up a required archive
# USAGE: archive_initialize_required $archive_identifier $archive_candidate[…]
# RETURN: 0 if the archive is found,
#         1 if it is missing
archive_initialize_required() {
	local archive_identifier
	archive_identifier="$1"
	shift 1

	local archive_candidate archive_path
	for archive_candidate in "$@"; do
		archive_path=$(archive_path "$archive_candidate")
		if [ -f "$archive_path" ]; then
			local archive_name
			archive_name=$(archive_name "$archive_candidate")
			export "${archive_identifier}_NAME=${archive_name}"
			export "${archive_identifier}_PATH=${archive_path}"
			## Cache the path to the candidate archive, to prevent the need to re-compute it later.
			export "${archive_candidate}_PATH=${archive_path}"
			## Export the legacy variable, its value can be expected by game scripts.
			export "${archive_identifier}=${archive_path}"
			## Set the extractor / type of the archive.
			local archive_extractor archive_extractor_options archive_type
			archive_extractor=$(archive_extractor "$archive_candidate")
			archive_extractor_options=$(archive_extractor_options "$archive_candidate")
			archive_type=$(archive_type "$archive_candidate")
			export "${archive_identifier}_EXTRACTOR=${archive_extractor}"
			export "${archive_identifier}_EXTRACTOR_OPTIONS=${archive_extractor_options}"
			export "${archive_identifier}_TYPE=${archive_type}"
			## Look for extra parts
			archive_initialize_extra_parts "$archive_candidate"
			## Update the list of archives that are going to be used
			archives_used_add "$archive_candidate"
			## Check the archives integrity
			archives_integrity_check
			return 0
		fi
	done

	# Throw an error if no archive candidate has been found
	error_archive_not_found "$@"
	return 1
}

# Set up an optional archive
# USAGE: archive_initialize_optional $archive_identifier $archive_candidate[…]
archive_initialize_optional() {
	local archive_identifier
	archive_identifier="$1"
	shift 1

	local archive_candidate archive_path
	for archive_candidate in "$@"; do
		archive_path=$(archive_path "$archive_candidate")
		if [ -f "$archive_path" ]; then
			local archive_name
			archive_name=$(archive_name "$archive_candidate")
			export "${archive_identifier}_NAME=${archive_name}"
			export "${archive_identifier}_PATH=${archive_path}"
			## Cache the path to the candidate archive, to prevent the need to re-compute it later.
			export "${archive_candidate}_PATH=${archive_path}"
			## Export the legacy variable, its value can be expected by game scripts.
			export "${archive_identifier}=${archive_path}"
			## Set the extractor / type of the archive.
			local archive_extractor archive_extractor_options archive_type
			archive_extractor=$(archive_extractor "$archive_candidate")
			archive_extractor_options=$(archive_extractor_options "$archive_candidate")
			archive_type=$(archive_type "$archive_candidate")
			export "${archive_identifier}_EXTRACTOR=${archive_extractor}"
			export "${archive_identifier}_EXTRACTOR_OPTIONS=${archive_extractor_options}"
			export "${archive_identifier}_TYPE=${archive_type}"
			## Look for extra parts
			archive_initialize_extra_parts "$archive_candidate"
			## Update the list of archives that are going to be used
			archives_used_add "$archive_candidate"
			## Check the archives integrity
			archives_integrity_check
			return 0
		fi
	done

	# No archive has been found, but this does not warrant an error
	return 0
}

# Set up a list of extra parts for a given archive
# USAGE: archive_initialize_extra_parts $archive
archive_initialize_extra_parts() {
	local archive
	archive="$1"

	local archive_part archive_part_name archive_part_path index
	for index in $(seq 1 99); do
		archive_part="${archive}_PART${index}"
		## This would fail if no archive part is expected at this index.
		## The output redirection must be done inside the subshell, or bash --posix will ignore it.
		archive_part_name=$(archive_name "$archive_part" 2>/dev/null) || true
		## Exit at the first unset archive part.
		if [ -z "$archive_part_name" ]; then
			return 0
		fi
		archive_part_path=$(archive_path "$archive_part")
		if [ -f "$archive_part_path" ]; then
			export "${archive_part}_PATH=${archive_part_path}"
			## Update the list of archives that are going to be used
			archives_used_add "$archive_part"
		else
			error_archive_not_found "$archive_part_name"
			return 1
		fi
	done
}

# Check the integrity of all archives
# USAGE: archives_integrity_check
archives_integrity_check() {
	local option_checksum
	option_checksum=$(option_value 'checksum')

	case "$option_checksum" in
		('md5')
			archives_integrity_check_md5
		;;
	esac
}

# Check the integrity of all archives, using MD5
# USAGE: archives_integrity_check_md5
archives_integrity_check_md5() {
	local archives_list archive archive_hash_expected archive_hash_computed
	archives_list=$(archives_used_list)
	for archive in $archives_list; do
		archive_hash_expected=$(archive_hash_md5 "$archive")
		## Skip archives that have no expected MD5 hash set.
		if [ -z "$archive_hash_expected" ]; then
			continue
		fi
		archive_hash_computed=$(archive_hash_md5_computed "$archive")
		if [ "$archive_hash_computed" != "$archive_hash_expected" ]; then
			local archive_path
			archive_path=$(archive_path "$archive")
			error_hashsum_mismatch "$archive_path"
			return 1
		fi
	done
}

# List all the archives that are going to be used
# USAGE: archives_used_list
# RETURN: a list of archive identifiers, one per line,
archives_used_list() {
	local archives_list
	archives_list="${PLAYIT_ARCHIVES_USED_LIST:-}"

	local archive
	for archive in $archives_list; do
		printf '%s\n' "$archive"
	done
}

# Add an archive to the list of archives that are going to be used
# USAGE: archives_used_add $archive
archives_used_add() {
	local archive
	archive="$1"

	local archives_list
	archives_list="$(archives_used_list)"

	export PLAYIT_ARCHIVES_USED_LIST="$archives_list
	$archive"
}

# Print the list of archives supported vby the current game script
# USAGE: archives_return_list
# RETURNS: the list of identifiers of the supported archives,
#          as a list of strings separated by spaces or line breaks
archives_return_list() {
	# If a list is already explicitely set, return early
	if ! variable_is_empty 'ARCHIVES_LIST'; then
		# ShellCheck false-positive
		# Possible misspelling: ARCHIVES_LIST may not be assigned. Did you mean archives_list?
		# shellcheck disable=SC2153
		printf '%s' "$ARCHIVES_LIST"
		return 0
	fi

	# Try to find archives using the ARCHIVE_BASE_xxx_[0-9]+ naming scheme
	local archives_list
	archives_list=$(set | sed --silent 's/^\(ARCHIVE_BASE\(_[0-9A-Z]\+\)*_[0-9]\+\)\(_NAME\)\?=.*/\1/p')
	if [ -n "$archives_list" ]; then
		printf '%s' "$archives_list"
		return 0
	fi

	# Fall back on trying to find archives using the old naming scheme
	if ! compatibility_level_is_at_least '2.21'; then
		archives_list=$(archives_return_list_legacy)
		if [ -n "$archives_list" ]; then
			printf '%s' "$archives_list"
			return 0
		fi
	fi

	error_no_archive_supported
	return 1
}

