#!/bin/sh

help () {
	echo "Usage: $0 init | track FILE | commit | send REMOTE | pull REMOTE | diff | log"
}

operation=$1

#out=/dev/stdout
out=/dev/null

if [ $# -ne 1 ] && [ $# -ne 2 ]; then
	help
	exit 1
fi

copy_patched_file () {
    path="$1"
    tmp_path="$2"
    if [ ! "$tmp_path" = "" ]; then
        path="./$tmp_path/$path"
    fi
	# File is not top level
	if [ ! "$(echo $path | cut -d/ -f3)" = "" ]; then
    	mkdir -p "$(echo $path | rev | cut -d/ -f2- | rev)"
   	fi
    cp .sortle/tmp/$(echo $path | rev | cut -d/ -f1 | rev) $path
}

check_if_dir_is_repo () {
	if [ ! -d .sortle ]; then
		echo "No repo is initialised here. Exiting."
		exit 1
	fi
}

check_if_remote_dir_is_repo () {
	if [ ! -d .remote_tmp/.sortle/ ]; then
		echo "Remote $target_remote is not a sortle repository. Exiting."
		rm -r .remote_tmp
		exit 1
	fi
}

compare_local_remote_repo () {
	direction=$1 # either send or pull
	if [ "$direction" = "send" ]; then
		mainpath=".sortle"
		subpath=".remote_tmp/.sortle"
	elif [ "$direction" = "pull" ]; then
		mainpath=".remote_tmp/.sortle"
		subpath=".sortle"
	else
		echo "Invalid argument to function compare_local_remote_repo"
		exit 1
	fi

	for file in $(ls $mainpath/initial); do
		# it's fine if a file is not present e.g. the repo is in an older state
		if [ ! -f ${subpath}/initial/${file} ]; then
			continue
		fi
		if [ ! "$(diff $mainpath/initial/$file $subpath/initial/$file)" = "" ]; then
			echo "Initial states of local and remote repository differ. Exiting."
			rm -r .remote_tmp
			exit 1
		fi
	done

	for commit in $(ls $mainpath | grep commit_*); do
		if [ ! -d $subpath/$commit ]; then
			continue
		fi
		for diff in $(ls $mainpath/$commit/); do
			if [ "$(diff $mainpath/$commit/$diff $subpath/$commit/$diff)" != "" ]; then
				echo "Local and remote repository diverge in $commit. Exiting."
				rm -r .remote_tmp
				exit 1
			fi
		done
	done
}

compare_num_commits () {
	direction=$1 # either send or pull
	if [ "$direction" = "send" ]; then
		compare_operator="-gt"
		offending_repo="Remote repository"
	elif [ "$direction" = "pull" ]; then
		compare_operator="-lt"
		offending_repo="Local repository"
	else
		echo "Invalid argument to function compare_num_commits"
		exit 1
	fi
	num_commits_remote=$(ls .remote_tmp/.sortle | grep commit_* | wc -l)
	num_commits_local=$(ls .sortle | grep commit_* | wc -l)
	if [ $num_commits_remote $compare_operator $num_commits_local ]; then
		echo "$offending_repo has more commits. Exiting."
		exit 1
	fi
}

# Get temporary versions of files in last saved state
patch_tmp_files () {
	if [ ! "$1" = "" ]; then
		last_commit="commit_$1"
		list_commits=$(ls .sortle/ | grep commit_* | sort -V | head -n $1)
	else
		last_commit=$(ls .sortle | grep commit_* | sort -V | tail -1)
		list_commits=$(ls .sortle/ | grep commit_* | sort -V)
	fi
	mkdir .sortle/tmp/
	if [ "$list_commits" = "" ]; then
    	cp .sortle/initial/* .sortle/tmp/
    	return
    fi
	for f in $(cat .sortle/$last_commit/files | rev | cut -d/ -f1 | rev); do
		cp .sortle/initial/$f .sortle/tmp/
	done
	for commit in $list_commits; do
		for patchfile in $(ls .sortle/$commit | grep .diff); do
			fname=$(echo $patchfile | rev | cut -c 6- | rev)
			if [ -f .sortle/tmp/$fname ]; then
				patch .sortle/tmp/$fname < .sortle/$commit/$patchfile > $out
			fi
		done
	done
}

if [ "$operation" = "init" ]; then
	mkdir -p .sortle/initial/
elif [ "$operation" = "track" ]; then
	check_if_dir_is_repo
	if [ $# -ne 2 ]; then
		help
	fi
	target_file=$2
	if [ "$(ls .sortle/initial | grep $target_file)" != "" ]; then
		echo "File $target_file is already tracked"
		exit 1
	fi
	cp $target_file .sortle/initial/
elif [ "$operation" = "commit" ]; then
	check_if_dir_is_repo
	patch_tmp_files
	commit_num=$(ls .sortle/ | grep commit | wc -l)
	commit_num=$((commit_num+1))
	mkdir .sortle/commit_$commit_num
	# Save diffs
	for file in $(ls .sortle/initial); do
		path=$(find -type f | grep -v "\.sortle" | grep "$file$" | \
			grep -v $(pwd | rev | cut -d/ -f1 | rev))
		if [ ! "$path" = "" ]; then
			echo $path >> .sortle/commit_$commit_num/files
			if [ ! "$(diff .sortle/tmp/$file $path)" = "" ]; then
				diff .sortle/tmp/$file $path > .sortle/commit_$commit_num/$file.diff
			fi
		fi
	done
	rm -r .sortle/tmp
	echo "author:\nsummary:\n" > .sortle/commit_$commit_num/info
	editor .sortle/commit_$commit_num/info
elif [ "$operation" = "send" ]; then
	check_if_dir_is_repo
	if [ $# -ne 2 ]; then
		help
		exit 1
	fi
	target_remote=$2
	scp -r $target_remote .remote_tmp > $out
	check_if_remote_dir_is_repo
	compare_num_commits send
	compare_local_remote_repo send
	for fpath in $(find .sortle/initial/ -type f); do
		if [ ! -f .remote_tmp/$fpath ]; then
			scp $fpath $target_remote/$fpath > $out
		fi
	done
	for commit in $(ls .sortle | grep commit_*); do
		if [ ! -d .remote_tmp/.sortle/$commit ]; then
			scp -r .sortle/$commit $target_remote/.sortle/ > $out
		fi
	done
	if [ -f .sortle/remote-run-after-send ]; then
		if [ ! -f .remote_tmp/.sortle/remote-run-after-send ] || \
			[ "$(diff .sortle/remote-run-after-send \
			  .remote_tmp/.sortle/remote-run-after-send)" != "" ]; then
			scp .sortle/remote-run-after-send $target_remote/.sortle/ > $out
		fi
	fi
	patch_tmp_files
	curr_commit=$(ls .sortle | grep commit | sort -V | tail -1)
	for path in $(cat .sortle/$curr_commit/files); do
    	copy_patched_file $path .tmp
    	scp -r .tmp/* $target_remote > $out
	done
	rm -r .tmp
	rm -r .sortle/tmp
	rm -r .remote_tmp
	if [ -f .sortle/remote-run-after-send ]; then
		hostname=$(echo $target_remote | cut -d: -f 1)
		repo_path=$(echo $target_remote | cut -d: -f 2)
		ssh $hostname "cd $repo_path && bash .sortle/remote-run-after-send"
   	fi
elif [ "$operation" = "pull" ]; then
	check_if_dir_is_repo
	if [ $# -ne 2 ]; then
		help
		exit 1
	fi
	target_remote=$2
	scp -r $target_remote .remote_tmp > $out
	check_if_remote_dir_is_repo
	compare_num_commits pull
	compare_local_remote_repo pull
	for fpath in $(find .remote_tmp/.sortle/initial/ -type f); do
		if [ ! -f "$(echo $fpath | sed 's/.remote_tmp\///')" ]; then
			cp $fpath $(echo $fpath | sed 's/.remote_tmp\///')
		fi
	done
	for commit in $(ls .remote_tmp/.sortle | grep commit_*); do
		if [ ! -d .sortle/$commit ]; then
			cp -r .remote_tmp/.sortle/$commit .sortle/
		fi
	done
	patch_tmp_files
	curr_commit=$(ls .sortle | grep commit | sort -V | tail -1)
	for path in $(cat .sortle/$curr_commit/files); do
    	copy_patched_file $path
	done
	rm -r .sortle/tmp
	rm -r .remote_tmp
elif [ "$operation" = "diff" ]; then
	patch_tmp_files
	for file in $(ls .sortle/tmp/); do
		path=$(find -type f | grep -v "\.sortle" | grep "$file$" | \
			grep -v $(pwd | rev | cut -d/ -f1 | rev))
		if [ "$path" = "" ]; then
		   	echo "# diff $file\nDeleted $file"
		   	continue
		fi
		if [ ! "$(diff $path .sortle/tmp/$file)" = "" ]; then
			echo "# diff $file"
			diff .sortle/tmp/$file $path --color
	   	fi
	done
	rm -r .sortle/tmp
elif [ "$operation" = "log" ]; then
	cnt=0
	for commit in $(ls .sortle/ | grep commit_* | sort -V -r); do
		if [ "$cnt" = "0" ]; then
			echo "# $commit"
		else
			echo "\n# $commit"
		fi
		cnt=$((cnt+1))
		cat .sortle/$commit/info | grep -v ^$
	done
elif [ "$operation" = "revert" ]; then
	if [ ! "$(sortle diff)" = "" ]; then
		echo "There are uncommited changes in this workspace. Commit before reverting."
		exit 1
	fi
	commit_num=$2
	if [ "$(echo $commit_num | grep -E '^[0-9]+')" = "" ]; then
		echo "Expected integer, got $commit_num"
		exit 1
	fi
	num_commits=$(ls .sortle | grep commit_* | wc -l)
	if [ $num_commits -lt $commit_num ]; then
		echo "Repository has $num_commits commits. Can't revert to $commit_num."
		exit 1
	fi
	patch_tmp_files $commit_num
	for path in $(cat .sortle/commit_$commit_num/files); do
    	copy_patched_file $path
	done
	rm -r .sortle/tmp/
elif [ "$operation" = "restore" ]; then
	file=$2
	if [ -f $file ]; then
		echo "File $file exists in workspace. Nothing to restore."
		exit 1
	elif [ ! -f .sortle/initial/$file ]; then
		echo "File $file is not tracked by sortle. Nothing to restore."
		exit 1
	fi
	last_commit_present=$(grep -r $file .sortle/commit_*/files | \
		sort -V | tail -1 | cut -d/ -f 2 | cut -d_ -f2)
	patch_tmp_files $last_commit_present
	path=$(cat .sortle/commit_$last_commit_present/files | grep $file)
	copy_patched_file $path
	rm -r .sortle/tmp/
else
	echo "Unknown operation $operation. Exiting."
	exit 1
fi