#!/bin/sh

# Directory tree to watch
tree="$1"

if [ -z "$tree" ]; then
	echo "Usage: $0 PATH" >&2
	exit 1
fi

# Directories with states
state_prev="$tree/.state"
state_new="$tree/.state-new"

# Files with parts of state
file_list="file_list" # list of all files:
dir_list="dir_list" # list of all directories:
file_sums="file_sums" # md5sums of all files, sorted by sum: MD5SUM<tab>FILENAME
dir_sums="dir_sums" # md5sums of lists of all directories, sorted by sum: MD5SUM<tab>DIRNAME

if ! [ -d "$state_prev" ]; then
	# First run
	mkdir "$state_prev"
	>"$state_prev/$file_list"
	>"$state_prev/$dir_list"
	>"$state_prev/$file_sums"
	>"$state_prev/$dir_sums"
fi

if [ -d "$state_new" ]; then
	echo "Dirty state. Check if I'm not running yet and delete $state_new if not." >&2
	exit 1
fi
mkdir "$state_new"


xfind="find -name '[^.]*'" # find skipping hidden files

# Gather new state
$xfind -type f | xargs md5sum | sed 's/  /\t/' | sort >"$state_new/$file_sums"
$xfind -type d | while read d; do
	echo "$(ls "$d" | md5sum | sed 's/-//')$d"
done | sed 's/  /\t/' | sort >"$state_new/$dir_sums"

cut -f 2- "$state_new/$file_sums" >"$state_new/$file_list"
cut -f 2- "$state_new/$dir_sums" >"$state_new/$dir_list"


# First check changes in directories list

comm -13 "$state/$dir_list" "$state_new/$dir_list" >"$state/dirs_added"
comm -23 "$state/$dir_list" "$state_new/$dir_list" >"$state/dirs_removed"
# We ignore "changed" dirs

join -j 1 "$state/$dir_sums" "$state_new/$dir_sums" |
	while read sum name1 name2; do
		[ "$name1" != "$name2" ] && echo -e "$name1\t$name2"
	done >"$state/dirs_renamed"


# Remove files from file_sums that are part of removed directories
sed 's#^.*$#^&/#' "$state/dirs_removed" >"$state/dirs_removed_regex"
grep -f "$state/dirs_removed_regex" "$state/$file_list" >"$state/${file_list}_pruned"
sed 's#^.*$#\t&/#' "$state/dirs_removed" >"$state/dirs_removed_regex"
grep -f "$state/dirs_removed_regex" "$state/$file_sums" >"$state/${file_sums}_pruned"


# Check changes in files list

comm -13 "$state/$file_list" "$state_new/$file_list" >"$state/files_added"
comm -23 "$state/$file_list" "$state_new/$file_list" >"$state/files_removed"

join -j 1 "$state/$file_sums" "$state_new/$file_sums" |
	while read sum name1 name2; do
		[ "$name1" != "$name2" ] && echo -e "$name1\t$name2"
	done >"$state/files_renamed"

# We will join files by names to detect changed md5sums
sort -k 2 "$state/$file_sums" >"$state/file_sums_by_name"
sort -k 2 "$state_new/$file_sums" >"$state_new/file_sums_by_name"
join -j 2 "$state/file_sums_by_name" "$state_new/file_sums_by_name" | cut -f 1 >"$state/files_changed"
