# Utilities for file system navigation

alias watchdir='watch -c -n 1 exa -l --icons'

# Useful when the current directory was recreated
alias recd='cd $PWD'
# [c]hange [d]irectory [e]xisting - cd into the first existing directory
cde() {
	local _dir="$1"
	while ! test -d "$_dir"
	do _dir="$(dirname "$_dir")"
	done
	cd "$_dir" "${@:2}"
}

# Go up a number of dirs
up() {
	if [[ $# < 1 ]] ; then
		cd ..
	else
		CDSTR=""
		for i in {1..$1} ; do
		CDSTR="../$CDSTR"
		done
		cd $CDSTR
	fi
}

# Switch directory & ls
cl() {
	builtin cd $1
	ls --almost-all --group-directories-first --file-type
}

# cd but search for data dirs and run ls after switching to target dir
cd() {
	if test -d "$DATA" && test "${1:--}" != "-" -a ! -d "$1" -a $# -eq 1
	then
		dir=$(fd --no-ignore --glob "$1*" "$DATA" --maxdepth 2 --type d --max-results 1 2>/dev/null)
		test -n "$dir" && cd "$dir"
	else
		builtin cd "$@" &&
		# we have to use two ls commands here, since:
		# - a single ls command hangs on multi-column formats when combined with color
		# - column malaligns output with ANSI escape sequences (view with LESS="" less)
		#   column --fillrows --output-width $(expr $COLUMNS + 20)
		#   pr -4 -l1 -W $COLUMNS -S"  "
		command ls --sort=none --quote-name |
			head -12 |
			xargs ls --sort=time --format=vertical --directory --literal --classify --color=always --width $COLUMNS |
			head -3
		code=$? && test $code -eq 141 || return $code
	fi
}

# LOCATE {{{1
# TODO no double heading with bat
alias fselect='fzf -0 -1 --reverse --height=40% --keep-right |
	  while read f; do test -d "$f" && cd "$f" || { highlight "$f" && print -s b \"$f\" && b "$f"; }; done';
loci() {
	locate --all --ignore-case --basename --existing "$@" |
		command grep --extended-regexp --ignore-case --color=always $(echo "$|${@:$#}" | sed 's/ /|/g') |
		fselect
}
alias loc='noglob loci'
alias uloc='noglob sudo updatedb && loci'
# locate exactly
locei() { locate --all --basename "\\$1" "$@" | fselect }
alias loce='noglob locei'
# locate all
alias loca='noglob sudo updatedb -l 0 --prunenames "" --prunefs "tmpfs sysfs debugfs" --prunepaths "" -o /var/cache/locate-all.db &&
	  sudo chmod +r /var/cache/locate-all.db &&
	  loci --database /var/cache/locate-all.db'
hunta() {
	unbuffer hunt -h "$@" / | less -RF +G
}

# ZOXIDE {{{1
alias c=z
# Listing for quick directory switcher based on zoxide and fzf
alias zoxide-list='noglob zoxide query -sl'
__zx_ls="ls --almost-all --color --human-readable --group-directories-first --file-type"
alias fzf-dir="fzf -0 -1 --tiebreak=index \
		--preview-window=30% --preview='$__zx_ls {}' \
		--height=80% --reverse --keep-right"
# fzf for zoxide - returns the selected path stripped of its score
zoxide-fzf() {
	fzf-dir -n2.. --preview="$__zx_ls {2..}" "$@" | sed 's|.* /|/|'
}
# Locate directories and add 0 zoxide score
locate-zox() {
	locate --basename --ignore-case "$@" |
		# TODO too slow
		# while read -r file; do test -d "$file" && echo "$file"; done |
		sed 's/^/   0 /'
}
# Switch directory heuristically using zoxide and locate
d() {
	test "$1" != "-" -a ! -d "$1" -a $# -eq 1 || { cd "$@" && return; }
	local query="$(zoxide-list "$@")"
	# First find out whether there is an obvious match
	# (score at least ten times above runner-up and score above 20)
	# which is not the current directory
	# If not select with fzf, using locate to find extra options
	target="$(if test -n "$query" &&
			expr "$(echo "$query" | sed 's| \+\([^ ]\+\).*|\1|;q')" \> 20 \& \
				$(echo "$query" | sed 'N;s|/.* \([0-9]\+\)|> 10 * \1|;q' | sed 's| */.*||') >/dev/null &&
			[[ ! "$(echo "$query" | head -1)" =~ ".*$PWD" ]]
			then echo "$query" | head -1
			else test -n "$query" && echo "$query"; locate-zox "$@"
		fi | zoxide-fzf)"
	test $? -lt 2 && test -z "$target" && sudo updatedb && target="$(locate --basename "$@" | fzf-dir)"
	test -n "$target" && cde "$target"
}
# Switch directory interactively using zoxide and locate
di() {
	test "$1" = "-" || local dir=$({ zoxide-list "$@"; locate-zox "$@" } | zoxide-fzf)
	cd "${dir:-$1}"
}
