#!/bin/sh

## DEFINITIONS
name="$(basename "$0")"
self="$(realpath "$0")"

globalconfigdir=/etc/xdg
case "$XDG_CONFIG_DIRS" in
	(""|*$globalconfigdir*);;
	(*) globalconfigdir=$(echo $XDG_CONFIG_DIRS | cut -d: -f1);;
esac

export dir_home="$(dirname $(find "$INSTALEE_HOME" \
	"${XDG_CONFIG_HOME:-$HOME/.config}/$name" "$HOME/.$name" "$globalconfigdir/instalee" "$(realpath "$(dirname "$0")")" "$PWD" \
	-maxdepth 1 -name "handlers.available*" 2>/dev/null | head -1))"
handlersfile="$dir_home/handlers.available"
dir_packages="$dir_home/packages"

highlight() { echo $2 "[4m[1m$1[0m" >&2; }
destress() { echo $2 "[2m$1[0m" >&2; }

# Cat a file, filtering out dos line endings and commented lines
catfile() {
	cat "$@" | tr -d '\r' | sed '/^#/d'
}

# Get the content of a file or the output of its execution
getcontent() {
	for arg; do
		if test -z "$noexec" && test -x "$arg"
		then "$arg"
		else catfile "$arg"
		fi
	done
}

# Get available package entries for given package
getentries() {
	catfile "$handlersfile" | while read handler
		do find "$dir_packages/$1/" -depth -type f -name "$handler*" 2>/dev/null | tac
		done
}

# Install a package by name
installpkg() {
	test "$1" = "--quiet" && local quiet=true && shift
	local dir_package="$dir_packages/$1/"
	# TODO prefer unsuffixed entries, i.e. sort by length
	local package_available_entries=""
	test -d "$dir_package" && package_available_entries="$(getentries "$1")"
	if test -z "$package_available_entries"; then
		local pkghandlers="$(find "$dir_package" -type f -printf "%f\n" 2>/dev/null)"
		for handler in $pkghandlers
		do installpkg --quiet "$handler" &&
				echo "$handler" >>"$handlersfile" &&
				package_available_entries=$(getentries "$1") &&
				break
		done
		if test -z "$package_available_entries"; then
			test "$quiet" ||
				echo "No handler available for package '$1'" >&2
			return 2
		fi
	fi
	local pkg_entry
	for pkg_entry in $package_available_entries
	do	highlight "Installing $(echo ${pkg_entry#$dir_packages/} | sed 's|\(.*\)/|\1 via |')"
		set -e
		local name="$(basename $pkg_entry)"
		local base="${name%_*}"
		echo "[1mDependencies[0m" >&2
		for dependency in $(cat "${dir_package}depends" "${dir_package}depends_${base}" 2>/dev/null)
		do { runinstalee "$dependency" || installdefault "$dependency"; } | sed 's/^/ /'
		done

		local ext="${name#$base}"
		local handler="$dir_home/handlers/$base"
		(cd "$(mktemp -d --tmpdir=$execdir "$name.XXXX")"
		if test -d "$handler"
		then
			# This declaration needs to be ahead, otherwise it overrides failing exit codes
			local args
			local install
			args="$(getcontent "$pkg_entry")"
			args="${args:-$1}"
			installhandler="$(find "$handler" -name "install$ext")"
			if test -n "$installhandler"
			then pkg_entry="$installhandler $args"
			else $(find "$handler" -name "${ext#_}") $args
				pkg_entry="$(find "$handler" -name "install") $1"
			fi
		else
			destress " running unhandled" -n
		fi
		destress " $pkg_entry"
		$pkg_entry
		find ${dir_package} -name "post" -o -name "post_${base}" -exec {} \; | sed 's/^/ /'
		)
		set +e
		break
	done
}

## EXECUTION

version=$(printf "$name r%s.%s%s" "$(git rev-list --count HEAD 2>/dev/null)" "$(git rev-parse --short HEAD 2>/dev/null)" "$(git diff-index HEAD 2>/dev/null | wc -l | sed 's|^[1-9]|-mod\0|')")
while true
do case "$1" in
	(-V|--version|"")
		printf "$version\nTry '$0 --help' or 'man $name' for usage info\n" >&2
		# TODO license https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion
		exit 0
		;;
	(-h|--help)
		man $name 2>/dev/null || man "$dir_home/$name.1"
		exit $?
		;;
	(-v|--verbose) set -x; export INSTALEE_VERBOSE=10; shift;;
	(-d|--default) default=true; shift;;
	(-x|--noexec) noexec=true; shift;;
	(-a|--add*)
		action=$1
		shift
		case "$action" in
		(*group) ${EDITOR:-vi} "$dir_home/groups/$1"; exit $?;;
		(*handler)
			file="$dir_home/handlers/$1/install"
			if ! test -f "$file"; then
				mkdir -p "$(dirname "$file")"
				printf "#!/bin/sh -e\n$1 install "'"$@"' >"$file"
			fi
			${EDITOR:-vi} "$file"
			exit $?;;
		esac
		if test $# -gt 0
		then packagename=$1; shift
		else printf "Name of the package? "; read packagename
		fi
		dir="$dir_packages/$packagename"
		mkdir -p "$dir"
		if test $# -gt 0
		then handler=$1
			 test $# -gt 1 && echo "$2">"$dir/$handler" || ${EDITOR:-vi} "$dir/$handler"
			 exit $?
		else
			while printf "Handler? (Ctrl-C or Ctrl-D to exit, / to list all) " && read handler
			do
				case "$handler" in
				(/*) find "$dir_packages" "$dir_home/handlers" -type f -printf "%f\n" |
					grep -vE '(_|\.)' |
					sort | uniq | paste -s -d ' ';;
				(*) ${EDITOR:-vi} "$dir/$handler";;
				esac
			done
			exit 0
		fi
		;;
	(*) break;;
esac
done

installdefault() {
	local defaulthandler="$(head -1 "$handlersfile")"
	highlight "Attempting install of '$1' via default handler '$defaulthandler'"
	$(test "$noexec" && echo "sh") "$dir_home/handlers/$defaulthandler/install" "$1" &&
		$(test "$noexec" && echo "sh") "$self" --add "$1" "$defaulthandler" "" &&
		echo "Added package entry for '$1' via '$defaulthandler'" >&2
}

runinstalee() {
	local groupfiles=$(find "$dir_home/groups/$1" -type f 2>/dev/null)
	if test -n "$groupfiles"
	then
		for package in $(getcontent $groupfiles)
		do runinstalee "$package" || installdefault "$package"
		done
	else installpkg "$1" || { test "$default" && installdefault "$1"; }
	fi
}

if ! test -f "$handlersfile"
then
	copy="$(case "$(uname)" in
			(MINGW64*) echo "$dir_home/handlers.available.windows";;
			(*) ls "$dir_home/$({ cat /etc/os-release | grep -E '^ID(_LIKE)?=' | cut -d= -f2 | grep . || lsb_release -s -i | tr 'A-Z' 'a-z'; } |
					sed 's/^/handlers.available./' | paste -d' ' -s)" 2>/dev/null | head -1;;
			esac)" &&
		test -n "$copy" &&
		echo "Bootstrapping available handlers from $copy" &&
		cp "$copy" "$handlersfile" || {
			# TODO copy to hostname suffix to prevent lock in on stick
			echo "Missing available handlers in $handlersfile!" >&2
			exit 1
		}
fi

chmod +rx "$dir_home/handlers" -R 2>/dev/null

execdir=/tmp/instalee.$(date +%s)
mkdir -p $execdir
cd $execdir
destress "Running $version in $execdir"

export INSTALEE_VERBOSE="${INSTALEE_VERBOSE:-2}"
exitcode=0
while test $# -gt 0; do
	runinstalee "$1"
	exitcode=$(expr $exitcode \| $?)
	shift
done
case $- in (*x*) $(which tree && echo " $execdir -L" || echo "find $execdir -maxdepth") 3;; esac
exit $exitcode
