# 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 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}"
			## Set archive version string.
			local archive_version
			archive_version=$(archive_version "$archive_candidate")
			export "${archive_identifier}_VERSION=${archive_version}"
			## 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
}

# Identify an optional archive from a list of candidates.
# 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
			# Run a MD5 hash comparison, to ensure the current archive is the expected one.
			# In case of hash mismatch, skip the current candidate.
			local option_checksum
			option_checksum=$(option_value 'checksum')
			case "$option_checksum" in
				('md5')
					if ! archive_hash_comparison "$archive_candidate"; then
						continue
					fi
				;;
			esac

			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}"
			## Set archive version string.
			local archive_version
			archive_version=$(archive_version "$archive_candidate")
			export "${archive_identifier}_VERSION=${archive_version}"
			## 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"
			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
	archives_list=$(archives_used_list)

	for archive in $archives_list; do
		if ! archive_hash_comparison "$archive"; then
			local archive_path
			archive_path=$(archive_path "$archive")
			error_hashsum_mismatch "$archive_path"
			return 1
		fi
	done
}

# Compare the hash of a given archive with the expected values.
# USAGE: archive_hash_comparison $archive
archive_hash_comparison() {
	local archive
	archive=$1

	# Fetch the list of valid hashes for the current archive.
	# If no valid hash is set to compare against, skip this verification.
	local archive_hashes_list
	archive_hashes_list=$(archive_hashes_list_md5 "$archive")
	if [ -z "$archive_hashes_list" ]; then
		return 0
	fi

	# Compute the archive hash.
	local archive_name archive_hash_computed
	archive_name=$(archive_name "$archive")
	archive_hash_computed=$(archive_hash_md5_computed "$archive_name")

	# Compare the archive hash to the list of valid ones,
	# stop verification as soon as a valid hash is matched.
	local archive_hash_valid
	for archive_hash_valid in $archive_hashes_list; do
		if [ "$archive_hash_computed" = "$archive_hash_valid" ]; then
			return 0
		fi
	done

	# If we reached this point, no valid hash has been matched.
	return 1
}

# 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 by the current game script
# The archive identifiers are separated by line breaks.
# USAGE: archives_list
archives_list() {
	# Generate a list of archives based on the ARCHIVE_BASE_xxx_[0-9]+ naming scheme
	archives_list=$(
		set |
			sed --silent 's/^\(ARCHIVE_BASE\(_[0-9A-Z]\+\)*_[0-9]\+\)\(_NAME\)\?=.*/\1/p' |
			sort --reverse --version-sort
	)

	if [ -z "$archives_list" ]; then
		error_no_archive_supported
		return 1
	fi

	## Errors due to empty grep output are ignored
	printf '%s\n' $archives_list | grep '^ARCHIVE_BASE_[0-9]\+' || true
	printf '%s\n' $archives_list | grep --invert-match '^ARCHIVE_BASE_[0-9]\+' || true
}

