#!/bin/sh
set -eu


TYPES='
git
pijul
fossil
bitkeeper
darcs
mercurial
cvs
'


guess_type() {
	if [ -e "$1"/.git ] || { [ -n "${GIT_DIR:-}" ] && [ -n "$GIT_WORK_TREE" ]; }; then
		echo git
	elif [ -e "$1"/.pijul ]; then
		echo pijul
	elif [ -e "$1"/.hg ]; then
		echo mercurial
	elif [ -e "$1"/.bk ]; then
		echo bitkeeper
	elif [ -e "$1"/_darcs ]; then
		echo darcs
	elif [ -e "$1"/CVS/ ]; then
		echo cvs
	elif [ -e "$1"/.fslckout ]; then
		echo fossil
	else
		return 1
	fi
}




git_fetch() {
	git fetch --all "$@"
}

fossil_fetch() {
	fossil pull "$@"
}

darcs_fetch() {
	darcs fetch "$@"
}

mercurial_fetch() {
	hg pull "$@"
}

cvs_fetch() {
	timeout 300 cvs update "$@"
}

git_status() {
	git status "$@"
}

fossil_status() {
	fossil status "$@"
	fossil extras "$@"
}

git_diff() {
	git diff "$@"
}

fossil_diff() {
	fossil diff --unified | diff-so-fancy | less -R
}

git_ps1() {
	BRANCH_NAME="$(git rev-parse --abbrev-ref HEAD)"
	OUT="$(git status --short --branch --porcelain)"
	BRANCH_LINE="$(printf '%s\n' "$OUT" | head -n 1)"
	DIFF_LINES="$(printf '%s\n' "$OUT" | tail -n +2)"

	IS_AHEAD=false
	IS_BEHIND=false
	if printf '%s\n' "$BRANCH_LINE" | grep -q 'ahead'; then
		IS_AHEAD=true
	fi
	if printf '%s\n' "$BRANCH_LINE" | grep -q 'behind'; then
		IS_BEHIND=true
	fi

	LINE=''

	if [ "$IS_AHEAD" = true ] && [ "$IS_BEHIND" = true ]; then
		LINE="^^^ $BRANCH_NAME vvv"
	elif [ "$IS_AHEAD" = true ]; then
		LINE="^ $BRANCH_NAME ^"
	elif [ "$IS_BEHIND" = true ]; then
		LINE="v $BRANCH_NAME v"
	else
		LINE="$BRANCH_NAME"
	fi

	HAS_DIFF=false
	HAS_UNTRACKED=false
	if printf '%s\n' "$DIFF_LINES" | grep -Eq '^([ADMRU ][MDU ]|[ADU][ADU])'; then
		HAS_DIFF=true
	fi
	if printf '%s\n' "$DIFF_LINES" | grep -Eq '^([?][?]| A)'; then
		HAS_UNTRACKED=true
	fi

	if [ "$HAS_DIFF" = true ]; then
		COLOR_FN=redb
		LINE="{$LINE}"
	elif [ "$IS_AHEAD" = true ] || [ "$IS_BEHIND" = true ]; then
		COLOR_FN=bluei
		LINE="[$LINE]"
	elif [ "$HAS_UNTRACKED" = true ]; then
		COLOR_FN=lightblue
		LINE="{$LINE}"
	else
		COLOR_FN=green
		LINE="($LINE)"
	fi

	color -c "$COLOR_FN" "$LINE"

	BRANCH_COUNT="$(git branch --list | wc -l)"
	if [ "$BRANCH_COUNT" -gt 1 ]; then
		color -c lightblue "<$BRANCH_COUNT>"
	fi

	STASH_COUNT="$(git stash list | wc -l)"
	if [ "$STASH_COUNT" != 0 ]; then
		color -c red "*$STASH_COUNT"
	fi

	color -c blacki " - git/$(git rev-parse HEAD)"
}

pijul_ps1() {
	LINE="$(pijul channel | awk '$1 == "*" && $0=$2')"

	DIFF_LINES="$(pijul diff -u -s)"
	HAS_DIFF=false
	HAS_UNTRACKED=false
	if printf '%s\n' "$DIFF_LINES" | grep -q '^[A|D|M|R| ][M|D| ]'; then
		HAS_DIFF=true
	fi
	if printf '%s\n' "$DIFF_LINES" | grep -Eq '^([?][?]| A)'; then
		HAS_UNTRACKED=true
	fi

	if [ "$HAS_DIFF" = true ]; then
		COLOR_FN=redb
		LINE="{$LINE}"
	elif [ "$IS_AHEAD" = true ] || [ "$IS_BEHIND" = true ]; then
		COLOR_FN=bluei
		LINE="[$LINE]"
	elif [ "$HAS_UNTRACKED" = true ]; then
		COLOR_FN=lightblue
		LINE="{$LINE}"
	else
		COLOR_FN=green
		LINE="($LINE)"
	fi

	color -c "$COLOR_FN" "$LINE"

	CHANNEL_COUNT="$(pijul channel | wc -l)"
	if [ "$CHANNEL_COUNT" -gt 1 ]; then
		color -c lightblue "<$CHANNEL_COUNT>"
	fi

	CHANGEID="$(pijul log --limit 1 --output-format json |
			jq -r '.[0].hash')"
	color -c blacki " - pijul/$CHANGEID"
}

bitkeeper_ps1() {
	printf ''
}

cvs_ps1() {
	printf ''
}

fossil_ps1() {
	BRANCH_NAME="$(fossil branch current)"

	if [ -n "$(fossil extras)" ]; then
		HAS_UNTRACKED=1
	fi

	BRANCH_MARKER="$BRANCH_NAME"

	if [ -n "${HAS_UNTRACKED:-}" ]; then
		COLOR_FN=lightblue
		LINE="($BRANCH_MARKER)"
	else
		COLOR_FN=green
		LINE="($BRANCH_MARKER)"
	fi

	color -c "$COLOR_FN" "$LINE"

	color -c blacki " - fossil/$(fossil info | awk '/^checkout:/ { print $2 }')"
}

mercurial_ps1() {
	BRANCH_NAME="$(hg branch)"
}

git_gc() {
	git remote prune origin "$@"
	git gc
}

git_ls() {
	git ls-files
}

pijul_ls() {
	pijul ls
}

fossil_ls() {
	fossil ls
}

git_clean() {
	git clean -nffdx
}

fossil_clean() {
	fossil clean -n -v -x --emptydirs
}


usage() {
	cat <<-'EOF'
		Usage:
		  vcs [-C DIR] ACTION
		  vcs [-C DIR] -t
		  vcs -l
		  vcs -h
	EOF
}

help() {
	cat <<-'EOF'


		Options:
		  -C DIR        change to DIR instead of PWD
		  -l            list the supported VCS types
		  -t            show the guesses VCS type
		  -h, --help    show this message

		  ACTION        the action to be performed on the repository:
		                - fetch
		                - ps1
		                - status
		                - gc
		                - ls


		Wrapper command for normalizing options of differenct version
		control software.


		Examples:

		  Say the type of repo in "project/":

		    $ vcs -C project/ -t


		  Fetch updates for VCS in current directory:

		    $ vcs fetch
	EOF
}


for flag in "$@"; do
	case "$flag" in
		(--)
			break
			;;
		(--help)
			usage
			help
			exit
			;;
		(*)
			;;
	esac
done

while getopts 'C:lth' flag; do
	case "$flag" in
		C)
			cd "$OPTARG"
			;;
		(l)
			# shellcheck disable=2086
			echo $TYPES | tr ' ' '\n'
			exit
			;;
		(t)
			guess_type "$PWD" || {
				printf 'Could not guess the type of the repository.\n' >&2
				exit 2
			}
			exit
			;;
		(h)
			usage
			help
			exit
			;;
		(*)
			usage >&2
			exit 2
	esac
done
shift $((OPTIND - 1))

ACTION="${1:-}"
eval "$(assert-arg -- "$ACTION" 'ACTION')"
shift

if [ "${1:-}" = '--' ]; then
	shift
fi

TYPE="$(guess_type "$PWD")"
if [ -z "$TYPE" ]; then
	printf 'Could not guess the type of the repository.\n' >&2
	exit 2
fi

CMD="$TYPE"_"$ACTION"
if ! command -v "$CMD" >/dev/null; then
	printf 'Invalid ACTION: "%s".\n\n' "$ACTION" >&2
	usage >&2
	exit 2
fi

"$CMD" "$@"
