#!/bin/sh

readonly XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
readonly CONFIGDIR="$XDG_CONFIG_HOME/tbuilder"
readonly POUDRIERE_DIR=${POUDRIERE_DIR:-/usr/local/poudriere}
readonly POUDRIERE_PORTS=${POUDRIERE_PORTS:-${POUDRIERE_DIR}/ports/default}

readonly command=$1
query=$2
readonly build=${3:-none}

interrupted=0 # build interrupted by ctrl+c

check_portname() {
	local flavor flavors port

	if [ "$1" = "." ]; then
		if [ ! "$(make -V PORTNAME)" ]; then
		       printf "===> %s: not a portdir\n\n" "$PWD" >&2
		       display_usage
		fi
		port=$(echo "$PWD" | awk '{
			n = split($0, p ,"/");
			print p[n-1] "/" p[n];
		}')
	else
		port=$1
	fi
	if [ ! -d "$POUDRIERE_PORTS/${port%%@*}" ]; then
		printf "===> %s: port directory does not exists\n\n" \
			"$POUDRIERE_PORTS/$port" >&2
		display_usage
	fi
	if [ "${port##*@}" != "${port%%@*}" ]; then
		flavors=$(make -C "$POUDRIERE_PORTS/${port%%@*}" -V FLAVORS)
		if [ "${port##*@}" = "all" ]; then
			for flavor in $flavors; do
				echo "${port%%@*}@$flavor"
			done
			return
		fi
	fi
	echo $port
}

display_usage() {
	<< EOF >&2 cat
Usage: ${0##*/} <command> <query> [name] [poudriere testport args]

 command: bdeps  - build ports having certain build dependency
          file   - build ports read from file
          grep   - build ports containing phrase in Makefile
          ldeps  - build ports linking with certain library
          match  - build ports with name matching certain phrase
          name   - build specific port
          slaves - build slaves of certain port

 query: (bdeps|match) - portname
        (file) - name of file to read category/port entries from
        (grep) - grep pattern
        (ldeps|name|slaves) - category/port or '.' for port from current directory

 name: poudriere jail name or profile filename

EOF
	exit 1
}

is_build_dep() {
	local dep hname oname test_str=$(opt_test_string $2)

	make -V BUILD_DEPENDS -V LIB_DEPENDS $test_str | grep -q $1 && return 0

	[ "$2" ] || return 1
	hname=$(grep $1 Makefile | cut -f1 -d=)
	oname=$(echo $2 | sed -E 's,(\+|\-),,')
	for dep in BUILD LIB; do
		[ "$oname" = "${hname%_${dep}_DEPENDS}" ] && return 0
	done

	return 1
}

is_other_dep() {
	local dep hname oname test_str=$(opt_test_string $2)

	make -V FETCH_DEPENDS -V EXTRACT_DEPENDS -V PATCH_DEPENDS \
		-V RUN_DEPENDS -V TEST_DEPENDS $test_str | grep -q $1 &&
		return 0

	[ "$2" ] || return 1
	hname=$(grep $1 Makefile | cut -f1 -d=)
	oname=$(echo $2 | sed -E 's,(\+|\-),,')
	for dep in FETCH EXTRACT PATCH RUN TEST; do
		[ "$oname" = "${hname%_${dep}_DEPENDS}" ] && return 0
	done

	return 1
}

opt_test_string() {
	case $1 in
	-*)
		echo "OPTIONS_DEFAULT=${1#-}" ;;
	+*)
		echo "OPTIONS_UNSET=${1#+}" ;;
	esac
}

poudriere_jails() {
	poudriere jail -lq | cut -d" " -f1
}

poudriere_test_build() {
	local jname=$1 port=${2%%:*} option=${2#*:}
	local port_cfg outcome="success"

	if [ "$port" != "$option" ]; then
		if [ "${option%:*}" != "OPT_UNKNOWN"  ]; then
			echo "*** port $port needs ${option%:*} set to ${option#*:} ***"
		else
			echo "*** port $port unknown option needed ***"
		fi
		port_cfg="-c"
	fi

	$SUDO poudriere testport -j $jname -o $port $poudriere_args $port_cfg
	[ $? -eq 0 ] || outcome="failed"
	[ $interrupted -eq 0 ] || outcome="interrupted"
	BUILD_RESULTS="$BUILD_RESULTS $port:$jname:$outcome"
}

[ "$query" ] || display_usage

if [ "$build" != "none" ] && ! poudriere_jails | grep -q $build \
	&& [ ! -f "$CONFIGDIR/$build" ]
then
	printf "===> %s: no such poudriere jail or profile\n\n" "$build" >&2
	display_usage
fi

if [ "$4" ]; then
	shift 3;
	poudriere_args="$*";
fi

if [ ! -d "$POUDRIERE_PORTS" ]; then
	echo "===> Poudriere ports directory not found: $POUDRIERE_PORTS" >&2
	echo "===> Try setting POUDRIERE_PORTS environment variable" >&2
	exit 1
fi

if [ $(id -u) -ne 0 ]; then
	if ! which sudo >/dev/null; then
		echo "===> sudo not found" >&2
		echo "===> This script requires root privileges or properly configured sudo." >&2
		exit 1
	fi
	SUDO="sudo"
fi

trap 'interrupted=1' INT

case $command in
bdeps)
	ports=$(pfind -b $query | cut -f1 -d' ') ;;
file)
	if [ ! -f "$query" ]; then
		echo "===> File $query does not exists" >&2
		exit 1
	fi
	ports=$(grep -v '^#' "$query") ;;
grep)
	ports=$(cd "$POUDRIERE_PORTS" && find . -name "Makefile*" -exec \
		grep -q "$query" {} \; -print | grep -v "$query/Makefile" |
		sed -E 's;(\./|/Makefile);;g') ;;
ldeps)
	query=$(check_portname $query)
	if [ "$query" ]; then
		tmp=$(cd "$POUDRIERE_PORTS" &&
			find . -name "Makefile*" -exec grep -q \
			"^[^#].*$query[ \\]*\$" {} \; -print | sed 's|\./||')
	fi
	for path in $tmp; do
		path=$(dirname "$path")
		[ "$query" != "$path" ] || continue
		cd "$POUDRIERE_PORTS/$path"
		if is_build_dep $query; then
			ports="$ports $path"
			continue
		fi
		is_other_dep $query && continue

		for option in $(make pretty-print-config); do
			case $option in -*)
				if is_build_dep $query $option; then
					ports="$ports $path:${option#-}:on"
					continue 2
				fi
				is_other_dep $query $option && continue 2
			esac
		done
		for option in $(make pretty-print-config); do
			case $option in +*)
				if is_build_dep $query $option; then
					ports="$ports $path:${option#+}:off"
					continue 2
				fi
			esac
		done
		ports="$ports $path:OPT_UNKNOWN:none"
	done
	;;
match)
	ports=$(pfind -n $query | cut -f1 -d' ') ;;
name)
	ports=$(check_portname $query) ;;
slaves)
	master=$(check_portname $query)
	[ "$master" ] && ports=$(pfind -s $master | cut -f1 -d' ') ;;
*)
	display_usage
esac
[ "$ports" ] || exit 1

for port in $ports; do
	case $build in
	none)
		option=${port#*:}
		if [ "$command" != "name" ]; then
			case ${option%:*} in
			"${port%%:*}")
				echo "Found match: ${port%%:*}" ;;
			"OPT_UNKNOWN")
				echo "Found match: ${port%%:*}, unknown option needed" ;;
			*)
				echo "Found match: ${port%%:*}, needs ${option%:*}=${option#*:}" ;;
			esac
		fi
		;;
	*)
		if poudriere_jails | grep -q $build; then
			queue=$build
		else
			queue=$(grep -v "^#" "$CONFIGDIR/$build")
		fi
		for b in $queue; do
			poudriere_test_build $b $port
			[ $interrupted -eq 0 ] || break 2
		done
	esac
done

if [ "$BUILD_RESULTS" ]; then
	printf "\n===> Following build(s) were performed:\n"
	printf "%-40s %-15s %s\n" "PORTNAME" "JAIL" "RESULT"

	for result in $BUILD_RESULTS; do
		params=${result#*:}
		printf "%-40s %-15s %s\n" "${result%%:*}" \
			"${params%:*}" "${params#*:}"
	done
	echo ""
fi

