#! /usr/bin/env bash

url="$1"
file="$2"
hash="$3"

if [ "${#}" != '3' ]; then
	echo 'Usage: download <url> <file> <hash>' >&2

	exit 1
fi

for tryDownloadProgram in wget curl; do
	tryDownloadProgramPath="$(which "${tryDownloadProgram}" 2>/dev/null)"

	if [ -z "${tryDownloadProgramPath}" ]; then
		continue
	fi

	if [ -x "${tryDownloadProgramPath}" ]; then
		downloadProgram="${tryDownloadProgram}"
		downloadProgramPath="${tryDownloadProgramPath}"

		break
	fi
done

case "${hash}" in
	????????????????????????????????????????)
		hashMethod='sha1'
		;;
	????????????????????????????????????????????????????????????????)
		hashMethod='sha256'
		;;
	-)
		hashMethod='null'
		;;
	*)
		echo "Unknown hash method for hash ${hash}" >&2

		exit 1
		;;
esac

function downloadFile() {
	local file urls
	local downloadProgramArgs
	local url
	local authoritativeURL

	file="$1"
	shift

	urls=("$@")

	authoritativeURL="${urls[@]: -1}"

	case "${downloadProgram}" in
		curl)
			downloadProgramArgs=(--header "X-Cache-URL: ${authoritativeURL}" --location --insecure --fail --output "${file}")
			;;
		wget)
			downloadProgramArgs=(--header="X-Cache-URL: ${authoritativeURL}" --no-check-certificate --output-document="${file}")
			;;
	esac

	for url in "${urls[@]}" __fail__; do
		rm -f "${file}"

		if [ "${url}" = '__fail__' ]; then
			return 1
		fi

		"${downloadProgram}" "${downloadProgramArgs[@]}" "${url}" && break
	done

	return 0
}

function verifyHash() {
	local file hash hashMethod
	local checkHash

	file="$1"
	hash="$2"
	hashMethod="$3"

	if [ "${hashMethod}" = 'null' ]; then
		return 0
	fi

	checkHash="$(openssl dgst "-${hashMethod}" "${file}" | sed 's@.*= *@@')"

	if [ "${checkHash}" = "${hash}" ]; then
		return 0
	fi

	echo "Hash (${hashMethod}) mismatch: Got: ${checkHash}; Expected: ${hash}" >&2

	return 1
}

rm -f "${file}.new" || exit 1

urls=("${url}")

if [ "${hashMethod}" != 'null' ]; then
	urls=(
		"http://hashcache.rkeene.org/${hashMethod}/${hash}"
		"${urls[@]}"
	)
fi

downloadFile "${file}.new" "${urls[@]}" || exit 1

verifyHash "${file}.new" "${hash}" "${hashMethod}" || exit 1

mv "${file}.new" "${file}" || exit 1

exit 0
