#!/bin/bash -efu
# Copy files from OLDDIR into NEWDIR with Debian archive-style
# checksum indirection.
IFS=$'\n'
readonly SCRIPTNAME='hashtow'
readonly INVNAME="$0"
readonly SCRIPT_DIR=$(dirname -- "$(realpath -e -- "$0")")
readonly FINDX="$SCRIPT_DIR/findx"
. gettext.sh
declare -rx TEXTDOMAIN='mirrorrib' TEXTDOMAINDIR="$SCRIPT_DIR/../../locale"

# Define helpers for the user interface.
function die {
    printf >&2 '%s %s: %s\n'\
     "$INVNAME" "$(gettext 'error')" "$(gettext "$1")"
    exit 1
}
function print_usage_message_and_exit {
    local USAGE EXAMPLE OLDDIR NEWDIR HASHDIR HASHREL
    readonly USAGE="$(gettext 'usage')"
    readonly EXAMPLE="$(gettext 'example')"
    readonly OLDDIR="$(gettext 'OLDDIR')" # the old directory
    readonly NEWDIR="$(gettext 'NEWDIR')" # the new directory
    # The HASHDIR is the directory into which hashes are to be written,
    # where, in this context, a *hash* is a file whose name is the checksum
    # of the file's contents.
    readonly HASHDIR="$(gettext 'HASHDIR')"
    # HASHREL is HASHDIR's path relative to NEWDIR
    readonly HASHREL="$(gettext 'HASHREL')"
    printf >&$1\
     '%s: %s [--dist] %s %s [%s %s]\n'\
     "$USAGE" "$INVNAME" "$OLDDIR" "$NEWDIR" "$HASHDIR" "$HASHREL"
    printf >&$1\
     '%s: %s %s\n'\
     "$EXAMPLE" "$INVNAME"\
     './mirror-primary{.src,}/dists/bullseye/main/source'
    printf >&$1 '%s: %s %s %s %s\n'\
     "$EXAMPLE" "$INVNAME" '--dist'\
     './mirror-primary{.src,}/dists/bullseye'\
     '{./mirror-primary,../..}/zzz-dists/stable'
    exit $(($1 - 1))
}
readonly -f die print_usage_message_and_exit

# Check the command line's layout.
(($#)) && { [ "$1" = '-?' ] || [ "$1" = '--help' ]; }\
 && print_usage_message_and_exit 1
declare -i IS_DIST=0
(($#)) && [ "$1" = '--dist' ] && { IS_DIST=1; shift; }
readonly IS_DIST
(($IS_DIST && $# == 4)) || ((!$IS_DIST && $# == 2))\
 || print_usage_message_and_exit 2

# Check for collisions between arguments.
readonly CHECK1=$(realpath -e -- "$1")
readonly CHECK2=$(realpath -e -- "$2")
[ "$CHECK2" == "$CHECK1" ] && die "NEWDIR must differ from OLDDIR"
declare CHECK3=; (($IS_DIST)) && CHECK3=$(realpath -e -- "$3")
readonly CHECK3
(($IS_DIST)) && [ "$CHECK3" = "$CHECK1" ]\
 && die "HASHDIR must differ from OLDDIR"

# Identify directories in which hashes are to be stored.
declare REL_HASH_PARENT='by-hash'
declare REL_HASH="$REL_HASH_PARENT/SHA256"
declare BY_HASH_PARENT="$2/$REL_HASH_PARENT"
declare BY_HASH="$2/$REL_HASH"
if (($IS_DIST)); then
    REL_HASH_PARENT="$4/.." REL_HASH=$4
    BY_HASH_PARENT="$3/.." BY_HASH=$3
fi
readonly REL_HASH_PARENT REL_HASH BY_HASH_PARENT BY_HASH

# Check that directories exist.  Clear the HASHDIR.
[ -d "$1" ] || die "OLDDIR does not exist"
[ -d "$2" ] || die "NEWDIR does not exist"
if (($IS_DIST)); then
    [ -d "$BY_HASH" ] || die "HASHDIR does not exist"
    set +f && rm -rfv -- "$BY_HASH/"*; set -f
else
    mkdir -pv -- "$BY_HASH"
fi

# Hash, copy and link files.
readonly POSTARGS='-mindepth 1 -maxdepth 1 -type f -printf '"'"'%P\n'"'"
declare SUM
for F in $("$FINDX" '' "$POSTARGS" "$1"); do
    if (($IS_DIST)); then
        cp -anv -- "$1/$F" "$BY_HASH/"
        ln -sv -- "$REL_HASH/$F" "$2/$F"
    else
        SUM=$(cat -- "$1/$F" | sha256sum | sed -r 's/\s*-\s*$//')
        cp -anv -- "$1/$F" "$BY_HASH/$SUM"
        ln -sv -- "$REL_HASH/$SUM" "$2/$F"
    fi
done
exit 0

