# List the directories from the game prefix that should be diverted to a persistent path
# USAGE: persistent_list_directories
# RETURNS: a list of paths to directories, separated by line breaks
#          glob patterns can be included
persistent_list_directories() {
	context_value 'USER_PERSISTENT_DIRECTORIES' | list_clean
}

# List the files from the game prefix that should be diverted to a persistent path
# USAGE: persistent_list_files
# RETURNS: a list of paths to files, separated by line breaks
#          glob patterns can be included
persistent_list_files() {
	context_value 'USER_PERSISTENT_FILES' | list_clean
}

# Populate the game prefix from the persistent storage
# USAGE: persistent_storage_initialization
persistent_storage_initialization() {
	## TODO: env --chdir could be used instead of cd in a subshell.
	## TODO: If $PATH_PERSISTENT does not exist yet, it should not be created here.
	##       Instead this while step should be skipped.
	cat <<- 'EOF'
	# Populate the prefix from persistent files
	mkdir --parents "$PATH_PERSISTENT"
	(
	    cd "$PATH_PERSISTENT"
	    find -L . -type f ! -path './wine/*' | while read -r file; do
	        persistent_file="${PATH_PERSISTENT}/${file}"
	        prefix_file="${PATH_PREFIX}/${file}"
	        if
	            [ ! -e "$prefix_file" ] ||
	            [ "$(realpath "$prefix_file")" != "$(realpath "$persistent_file")" ]
	        then
	            mkdir --parents "$(dirname "$prefix_file")"
	            ln --symbolic --force --no-target-directory \
	                "$persistent_file" \
	                "$prefix_file"
	        fi
	    done
	    unset file persistent_file prefix_file
	)

	EOF
}

# Set the common actions required for directories and files diversion to persistent storage
# This is required for the diversions using one of the following variables:
# - USER_PERSISTENT_DIRECTORIES
# - USER_PERSISTENT_FILES
# USAGE: persistent_storage_common
persistent_storage_common() {
	local persistent_list_directories persistent_list_files
	persistent_list_directories=$(persistent_list_directories)
	persistent_list_files=$(persistent_list_files)

	# Return early if the current game script does not use paths diversion
	if
		[ -z "$persistent_list_directories" ] &&
		[ -z "$persistent_list_files" ]
	then
		return 0
	fi

	cat <<- 'EOF'
	# Expand a path pattern into a list of existing paths.
	# If the pattern can not be expanded, it is printed as-is instead.
	expand_path_pattern() {
	    pattern="$1"

	    ## Silently skip empty patterns
	    if [ -z "$pattern" ]; then
	        return 0
	    fi

	    expanded_paths=$(find . -path "./${pattern#./}")
	    if [ -n "$expanded_paths" ]; then
	        printf '%s\n' "$expanded_paths"
	    else
	        printf '%s\n' "$pattern"
	    fi

	    unset pattern expanded_paths
	}

	EOF
}

# Set the action used to divert a given path to persistent storage
# USAGE: persistent_path_diversion
persistent_path_diversion() {
	## TODO: env --chdir could be used instead of cd in a subshell.
	cat <<- 'EOF'
	# Replace a given directory in a prefix by a link to another directory in persistent storage
	# USAGE: persistent_path_diversion $path_source $path_destination $directory
	persistent_path_diversion() {
	    path_source="$1"
	    path_destination="$2"
	    directory="$3"

	    ## If the target directory does not already exist in persistent storage,
	    ## copy it from the prefix (if existing) or create a new empty one.
	    if [ ! -e "${path_destination}/${directory}" ]; then
	        if [ -e "${path_source}/${directory}" ]; then
	            (
	                cd "$path_source"
	                mkdir --parents "$path_destination"
	                cp --dereference --parents --recursive \
	                    "$directory" \
	                    "$path_destination"
	            )
	        else
	            mkdir --parents "${path_destination}/${directory}"
	        fi
	    fi

	    ## Replace the directory in the prefix by a link to the one in persistent storage.
	    if [ ! -h "${path_source}/${directory}" ]; then
	        directory_parent=$(dirname "${path_source}/${directory}")
	        rm --recursive --force "${path_source:?}/${directory}"
	        mkdir --parents "$directory_parent"
	        ln --symbolic "${path_destination}/${directory}" "${path_source}/${directory}"
	    fi
	    unset directory_parent

	    unset path_source path_destination directory
	}

	EOF
}

# Update directories diversions to persistent storage
# USAGE: persistent_storage_update_directories
persistent_storage_update_directories() {
	local persistent_list_directories
	persistent_list_directories=$(persistent_list_directories)

	# Return early if the current game script does not use directories diversion
	if [ -z "$persistent_list_directories" ]; then
		return 0
	fi

	cat <<- EOF
	# Update directories diversions to persistent storage
	USER_PERSISTENT_DIRECTORIES='${persistent_list_directories}'
	EOF
	## TODO: env --chdir could be used instead of cd in a subshell.
	cat <<- 'EOF'
	(
	    cd "$PATH_PREFIX"
	    while read -r directory_pattern; do
	        ## Skip empty patterns
	        if [ -z "$directory_pattern" ]; then
	            continue
	        fi

	        while read -r directory; do
	            persistent_path_diversion "$PATH_PREFIX" "$PATH_PERSISTENT" "$directory"
	        done <<- EOL
	        $(expand_path_pattern "$directory_pattern")
	        EOL
	    done <<- EOL
	    $(printf '%s' "$USER_PERSISTENT_DIRECTORIES")
	    EOL
	    unset directory_pattern
	)

	EOF
}

# Update files diversions to persistent storage
# USAGE: persistent_storage_update_files
persistent_storage_update_files() {
	local persistent_list_files
	persistent_list_files=$(persistent_list_files)

	# Return early if the current game script does not use files diversion
	if [ -z "$persistent_list_files" ]; then
		return 0
	fi

	cat <<- EOF
	# Update files diversions to persistent storage
	USER_PERSISTENT_FILES='${persistent_list_files}'
	EOF
	## TODO: env --chdir could be used instead of cd in a subshell.
	cat <<- 'EOF'
	(
	    cd "$PATH_PREFIX"
	    while read -r file_pattern; do
	        ## Skip empty patterns
	        if [ -z "$file_pattern" ]; then
	            continue
	        fi

	        while read -r file; do
	            ## If the target file does not already exist in persistent storage,
	            ## copy it from the prefix (if existing).
	            if [ ! -e "${PATH_PERSISTENT}/${file}" ]; then
	                if [ -e "$file" ]; then
	                    cp --dereference --parents \
	                        "$file" \
	                        "${PATH_PERSISTENT}"
	                fi
	            fi

	            ## Replace the file in the prefix by a link to the one in persistent storage,
	            ## if such a file already exists in persistent storage.
	            if [ -e "${PATH_PERSISTENT}/${file}" ]; then
	                file_parent=$(dirname "$file")
	                rm --force "$file"
	                mkdir --parents "$file_parent"
	                ln --symbolic "${PATH_PERSISTENT}/${file}" "$file"
	            fi
	        done <<- EOL
	        $(expand_path_pattern "$file_pattern")
	        EOL
	        unset file
	    done <<- EOL
	    $(printf '%s' "$USER_PERSISTENT_FILES")
	    EOL
	    unset file_pattern
	)

	EOF
}

# Update persistent storage with files from the current prefix
# USAGE: persistent_storage_update_files_from_prefix
persistent_storage_update_files_from_prefix() {
	local persistent_list_files
	persistent_list_files=$(persistent_list_files)

	# Return early if the current game script does not use files diversion
	if [ -z "$persistent_list_files" ]; then
		return 0
	fi

	## TODO: env --chdir could be used instead of cd in a subshell.
	cat <<- 'EOF'
	# Update persistent storage with files from the current prefix
	(
	    cd "$PATH_PREFIX"
	    while read -r path_pattern; do
	        ## Skip empty patterns
	        if [ -z "$path_pattern" ]; then
	            continue
	        fi

	        while read -r path; do
	            if [ -f "$path" ] && [ ! -h "$path" ]; then
	                cp --parents --remove-destination "$path" "$PATH_PERSISTENT"
	                rm --force "$path"
	                ln --symbolic "${PATH_PERSISTENT}/${path}" "$path"
	            fi
	        done <<- EOL
	        $(expand_path_pattern "$path_pattern")
	        EOL
	        unset path
	    done <<- EOL
	    $(printf '%s' "$USER_PERSISTENT_FILES")
	    EOL
	    unset path_pattern
	)

	EOF
}

