#!/bin/sh

set -e

warn() {
	echo "$@" >&2
}

die() {
	warn "$@"
	exit 1
}

me=`basename $0`
usage() {
	echo >&2 "Usage:"
	echo >&2 "    $me create <coursename> [<cardfile>...]"
	echo >&2 "    $me destroy <coursename>"
	echo >&2 "    $me add-cards <coursename> <cardfile> [<cardfile>...]"
	echo >&2 "    $me remove-card <coursename> <cardhash>"
	echo >&2 "    $me verify <coursename>"
	echo >&2 "    $me dump   <coursename> <dir>"
	echo >&2 "    $me list"
	echo >&2 "    $me show-course <coursename>"
	echo >&2 "    $me update-card <coursename> <cardhash> <new-lesson-val> <new-period-val>"
	echo >&2 "    $me edit-card <coursename>"
	echo >&2 "    $me show-card <cardhash>"
	exit 1
}

[ "$#" -ge 1 ] || usage
action="$1"

conf="/etc/memorize-flashcards/conf"
[ -e "$conf" ] || die "E: Missing configuration: $conf"

read_conf_var() {
	local varname="$1"
	grep "^$varname\s\+" "$conf" | awk '{print $2}'
}

dbroot="`read_conf_var DBROOT`"
create_empty_db() {
	local coursename="$1"
	local coursetable="$dbroot/${coursename}.db"
	# Sanity: Make sure course db is not already there
	[ -e "$coursetable" ] && die "E: Course already exists: $coursetable (To add cards use the action: update)"

	## Create new course database and an empty cards table
	touch "$coursetable"
	echo "# Hash\t\t\t\t\t\tPath\t\t\t\t\t\t\t\t\t\tLesson\tPeriod" > "$coursetable"
}

cards_dir=`read_conf_var CARDSROOT`
cards_table="$cards_dir/cards.table"
increase_refcount() {
	local cardhash="$1"
	local cardfile="$2"
	local carddest="$3"

	if grep "^$cardhash" "$cards_table" --quiet; then
		local refcount=`awk  -vcardhash="$cardhash" '$1 == cardhash { print $3 }' "$cards_table"`
		refcount=$((refcount+1))
		sed -i "s,${cardhash}.*,${cardhash}\t`basename ${carddest}`\t${refcount}," $cards_table
	else
		echo "$cardhash\t`basename ${carddest}`\t1" >> "$cards_table"
		echo "I: Copy $cardfile --> $carddest"
		cp "$cardfile" "$carddest"
	fi
}

add_card() {
	local coursename="$1"
	local cardfile="$2"
	local coursetable="$dbroot/${coursename}.db"
	[ -f "$coursetable" ] || die "E: Missing course table: $coursetable"
	if ! [ -f "$cardfile" ]; then
		echo >&2 "W: Missing card file: $cardfile"
		return
	fi

	local cardhash=`md5sum "$cardfile" | awk '{print $1}'`
	if grep --silent "^$cardhash" "$coursetable"; then # Card already exists in course table, nothing to do
		echo >&2 "W: $cardfile already exists in course table"
		return
	fi

	local carddest="$cards_dir/${cardhash}.card"
	# Increase reference count of the card in cards.list, copy card file if needed
	increase_refcount "$cardhash" "$cardfile" "$carddest"

	# Add card record to course table
	echo "I: Insert card record ('$carddest', '$cardhash', -, -)"
	echo "$cardhash\t\t$carddest\t-\t-" >> "$coursetable"
}

decrease_refcount() {
	local cardhash="$1"
	local refcount=`awk  -vcardhash="$cardhash" '$1 == cardhash { print $3 }' "$cards_table"`
	if [ -z "$refcount" ]; then
		warn "W: Card '$cardhash' is missing from '$cards_table'"
		return
	fi
	local carddest_basename=`awk  -vcardhash="$cardhash" '$1 == cardhash { print $2 }' "$cards_table"`
	local carddest="`dirname $cards_table`/$carddest_basename"
	local refcount=$((refcount-1))
	if [ "$refcount" -gt 0 ]; then
		echo "I: Decreasing reference count in '$cards_table'"
		sed -i "s,${cardhash}.*,${cardhash}\t`basename ${carddest}`\t${refcount}," $cards_table
	else
		echo "I: Reference count reached 0"
		echo "I: \t>removing from '$cards_table'"
		sed -i "/${cardhash}.*/d" $cards_table
		echo "I: \t>removing from '$carddest'"
		rm "$carddest"
	fi
}

update_card() {
	local coursename="$1"
	local cardhash="$2"
	local lesson="$3"
	local period="$4"

	local coursetable="$dbroot/${coursename}.db"
	[ -f "$coursetable" ] || die "E: Course missing (searched $coursetable)"
	local record="" # For unknown reason I failed to init local record with grep at one line
	record=$(grep "^$cardhash" "$coursetable")
	local carddest=$(echo "$record" | awk '{print $2}')
	local new_record="$cardhash\t\t$carddest\t$lesson\t$period"
	sed -i "s,^$cardhash.*,$new_record," "$coursetable"
}

remove_card() {
	local coursename="$1"
	local cardhash="$2"
	local coursetable="$dbroot/${coursename}.db"
	[ -f "$coursetable" ] || die "E: Missing course table: $coursetable"

	if ! grep --silent "^$cardhash" "$coursetable"; then # Card is not in course table
		echo >&2 "E: '$cardhash' does not exist exists in course table"
		return
	fi

	# Decrease reference count of the card in cards.list, remove card file if needed
	decrease_refcount "$cardhash"

	# Remove card record from course table
	echo "I: Removing card record from course table: $coursetable"
	sed -i "/^${cardhash}.*/d" $coursetable
}

edit_card() {
	local cardhash="$1"
	grep "^$cardhash" "$cards_table" --quiet || die "E: Card not found in cards table ($cards_table)"

	local cardbase=`awk  -vcardhash="$cardhash" '$1 == cardhash { print $2 }' "$cards_table"`
	local cardfile="$cards_dir/$cardbase"
	[ -f "$cardfile" ] || die "E: Card table pointing to missing file: $cardfile"

	local modifiedcard=`mktemp`
	cp "$cardfile" "$modifiedcard"
	chmod 644 "$modifiedcard"
	"$EDITOR" "$modifiedcard"
	local modifiedhash=`md5sum "$modifiedcard" | awk '{print $1}'`
	if [ "$modifiedhash" = "$cardhash" ]; then
		echo "W: Card hasn't changed. Nothing to do"
	else
		for coursetable in `ls "$dbroot"/*.db`; do
			local coursename=`echo $coursetable | sed -e "s,$dbroot/,," -e 's,.db$,,'`
			grep --silent "^$cardhash" "$coursetable" || continue

			local old_record=""
			old_record=`grep "^$cardhash" "$coursetable"`
			# Preserve existing (lesson, period) values
			local lesson=$(echo "$old_record" | awk '{print $3}')
			local period=$(echo "$old_record" | awk '{print $4}')

			echo "I: Adding  $modifiedhash to $coursename"
			add_card "$coursename" "$modifiedcard"

			echo "I: Updating  $modifiedhash ($lesson, $period)"
			update_card "$coursename" "$modifiedhash" "$lesson" "$period"

			echo "I: Removing: $cardhash"
			remove_card "$coursename" "$cardhash"
		done
	fi
}

clean_comments() {
	sed -e 's,#.*,,' -e '/^$/d'
}

case "$action" in
	create)
		[ "$#" -ge 2 ] || usage
		coursename="$2"
		create_empty_db "$coursename"
		shift
		shift
		[ -f "$cards_table" ] || touch "$cards_table"
		for cardfile in "$@"; do
			add_card "$coursename" "$cardfile"
		done
		;;
	destroy)
		[ "$#" -eq 2 ] || usage
		coursename="$2"
		coursetable="$dbroot/${coursename}.db"
		cat "$coursetable" | clean_comments | while read cardhash carddest lesson period; do 
			decrease_refcount "$cardhash"
		done
		rm "$coursetable"
		;;
	add-cards)
		[ "$#" -ge 3 ] || usage
		coursename="$2"
		coursetable="$dbroot/${coursename}.db"
		[ -e "$coursetable" ] || die "E: Course missing (searched $coursetable)"
		shift
		shift
		for cardfile in "$@"; do
			add_card "$coursename" "$cardfile"
		done
		;;
	remove-card)
		[ "$#" -eq 3 ] || usage
		coursename="$2"
		cardhash="$3"
		remove_card "$coursename" "$cardhash"
		;;
	verify)
		[ "$#" -eq 2 ] || usage
		coursename="$2"
		die "$action: Not yet implemented"
		;;
	dump)
		[ "$#" -eq 3 ] || usage
		coursename="$2"
		destdir="$3"
		die "$action: Not yet implemented"
		;;
	list)
		ls -1 "$dbroot"/*.db 2>/dev/null | sed -e "s,$dbroot/,," -e 's,.db$,,' || :
		;;
	show-course)
		[ "$#" -eq 2 ] || usage
		coursename="$2"
		coursetable="$dbroot/${coursename}.db"
		[ -f "$coursetable" ] || die "E: Course missing (searched $coursetable)"
		cat "$coursetable"
		;;
	update-card)
		[ "$#" -eq 5 ] || usage
		coursename="$2"
		cardhash="$3"
		lesson="$4"
		period="$5"

		update_card "$coursename" "$cardhash" "$lesson" "$period"
		;;
	edit-card)
		[ "$#" -eq 2 ] || usage
		cardhash="$2"
		[ -n "$EDITOR" ] || die "E: No deafult editor (EDITOR env variable empty)"
		[ -x "`which $EDITOR`" ] || die "E: '$EDITOR' is not execuable (check EDITOR env variable)"
		edit_card "$cardhash"
		;;
	show-card)
		[ "$#" -eq 2 ] || usage
		cardhash="$2"
		grep "^$cardhash" "$cards_table" --quiet || die "E: Card not found in cards table ($cards_table)"
		cardbase=`awk  -vcardhash="$cardhash" '$1 == cardhash { print $2 }' "$cards_table"`
		cardfile="$cards_dir/$cardbase"
		[ -f "$cardfile" ] || die "E: Card table pointing to missing file: $cardfile"
		cat "$cardfile"
		;;
	*)
		die "E: Unknown action: $action"
		;;
esac
