#!/bin/bash

##
## binaryDiv's dotfiles management script
## Version 0.3.0
##

usage() {
	cat <<- END_OF_HELPTEXT
	Usage: $(basename $0) COMMAND [...]
	Manage dotfiles via git.

	General commands:
	  help          print this help message

	Installation:
	  install       run this on first start: install dotfiles script in ~/bin, configure git, ...
	                (this will NOT create links for all dotfiles, though)

	Dotfile management:
	  ls [DIR]      list contents of .dotfiles directory (or DIR relative to it)
	  diff FILE     use vimdiff to compare ~/FILE to ~/.dotfiles/FILE
	  link FILE...  create a symlink from ~/FILE to ~/.dotfiles/FILE (multiple files possible)

	Synchronization:
	  git ARGS...   wrapper for git (all arguments are passed on to git)
	  pull          shortcut for git pull
	END_OF_HELPTEXT
}


# Define root dir for dotfiles management (defaults to $HOME, can be overridden
# with environment variables for debugging)
if [ -z "$DOTFILESROOT" ]; then
	DOTFILESROOT="$HOME"
fi

# Set variables
homedir="$DOTFILESROOT"
dotfilesdir="$DOTFILESROOT/.dotfiles"


# Get command
[ $# -gt 0 ] || { usage; exit 1; }
cmd=$1
shift

# Parse command
case "$cmd" in
	help|--help|-h)
		usage
		exit 1
	;;

	install)
		bin_source="$dotfilesdir/bin/dotfiles"
		bin_target="$homedir/bin/dotfiles"
		bin_dir="$homedir/bin"

		if [ ! -e "$bin_target" ]; then
			echo "* Installing dotfiles script in $bin_dir/..."
			mkdir -pv "$bin_dir"
			ln -sr "$bin_source" "$bin_target" || exit 1
		elif [ ! "$(realpath $bin_target)" -ef "$bin_source" ]; then
			echo "! Warning: File $bin_target already exists but is NOT a link to $bin_source"
		fi

		if [[ ":$PATH:" != *":$bin_dir:"* ]]; then
			echo "! Warning: $bin_dir is not in your PATH."
		fi

		echo "* Configuring git settings..."
		git -C "$dotfilesdir" config merge.ff only
	;;

	ls)
		lsdir="$dotfilesdir"
		[ -n "$1" ] && lsdir="$lsdir/$1"

		echo "$lsdir:"
		ls -lAh --color=auto "$lsdir"
	;;

	# TODO: Command 'status' or 'linkstatus' to show which files are linked and which are not

	diff)
		[ -n "$1" ] || { echo "$(basename $0) $cmd: Missing argument."; exit 1; }
		file_A="$homedir/$1"
		file_B="$dotfilesdir/$1"

		if [ "$(realpath "$file_A")" -ef "$file_B" ]; then
			echo "$file_A is a link to $file_B"
		else
			vimdiff "$file_A" "$file_B"
		fi
	;;

	link)
		[ -n "$1" ] || { echo "$(basename $0) $cmd: Missing argument."; exit 1; }

		for filename in "$@"; do
			file_home="$homedir/$filename"
			file_dotfiles="$dotfilesdir/$filename"

			if [ ! -e "$file_dotfiles" ]; then
				echo "! $file_dotfiles: target file does not exist"
				exit 1
			elif [ -e "$file_home" ]; then
				if [ ! "$(realpath "$file_home")" -ef "$file_dotfiles" ]; then
					echo "! $file_home already exists but is NOT a link to $file_dotfiles, please fix manually"
					exit 1
				fi
			else
				echo "* Creating link $file_home -> $file_dotfiles"
				ln -sr "$file_dotfiles" "$file_home" || exit 1
			fi
		done
	;;
	
	git)
		git -C "$dotfilesdir" "$@"
	;;

	pull)
		git -C "$dotfilesdir" pull "$@"
	;;

	bash-completion)
		cat <<- 'END_OF_BASHCOMPLETION'
		_dotfiles_filenames() {
			compopt -o filenames
			files=($(compgen -f -X '*/@(.|..|.git)' .dotfiles/$cur))
			COMPREPLY=(${files[@]#.dotfiles/})
		}
		_dotfiles() {
			COMPREPLY=()
			local cur="${COMP_WORDS[COMP_CWORD]}"
			local commands="help install ls diff link git pull"
			local gitcommands="add commit diff fetch log pull push rebase status"

			if [[ $COMP_CWORD -eq 1 ]]; then
				COMPREPLY=($(compgen -W "$commands" -- $cur))
			else
				case "${COMP_WORDS[1]}" in
					ls|diff)
						[[ $COMP_CWORD -eq 2 ]] && _dotfiles_filenames
					;;

					link)
						_dotfiles_filenames
					;;

					git)
						if [[ $COMP_CWORD -eq 2 ]]; then
							COMPREPLY=($(compgen -W "$gitcommands" -- ${cur}))
						else
							_dotfiles_filenames
						fi
					;;
				esac
			fi
		}

		complete -F _dotfiles dotfiles
		END_OF_BASHCOMPLETION
	;;

	*)
		echo "Unknown command '$cmd', use 'help' to show all commands."
		exit 1
	;;
esac

