#!/bin/bash

# check environment
if [ ${GLD_ETC:-none} == none ]; then
	echo "ERROR: GLD_ETC is not exported from the shell environment"
	exit 1
fi

# configurable parameters
GITHUB="https://github.com"
GITHUBUSERCONTENT="https://raw.githubusercontent.com"
ORGANIZATION="US/CA/SLAC"
GITUSER=slacgismo
GITREPO="gridlabd-library"
GITBRANCH="master"
SVNLSOPTIONS="--config-option=servers:global:http-timeout=60 --non-interactive -rHEAD"
DATADIR="$GLD_ETC/library/$ORGANIZATION"
OPENURLCMD='open ${file}'
FORMAT="default"

# load the configuration (if any)
CONFIGFILE="$GLD_ETC/gridlabd-library.conf"
if [ -f $CONFIGFILE ]; then
	source $CONFIGFILE
fi

function error()
{
	RC=$1
	shift
	echo "ERROR: $*" >/dev/stdout
	exit $RC
}

function config()
{
	if [ $# -eq 0 -o "$1" == "show" ]; then
		if [ "$FORMAT" == "json" ]; then
			echo "{"
			echo "	\"GITHUB\" : \"$GITHUB\","
			echo "	\"GITHUBUSERCONTENT\" : \"$GITHUBUSERCONTENT\","
			echo "	\"ORGANIZATION\" : \"$ORGANIZATION\","
			echo "	\"GITUSER\" : \"$GITUSER\","
			echo "	\"GITREPO\" : \"$GITREPO\","
			echo "	\"GITBRANCH\" : \"$GITBRANCH\","
			echo "	\"CACHEDIR\" : \"$CACHEDIR\","
			echo "	\"DATADIR\" : \"$DATADIR\","
			echo "	\"SVNLSOPTIONS\" : \"$SVNLSOPTIONS\""
			echo "}"
		elif [ "$FORMAT" == "glm" ]; then
			echo "#define LIBRARY_GITHUB=$GITHUB"
			echo "#define LIBRARY_GITHUBUSERCONTENT=$GITHUBUSERCONTENT"
			echo "#define LIBRARY_ORGANIZATION=$ORGANIZATION"
			echo "#define LIBRARY_GITUSER=$GITUSER"
			echo "#define LIBRARY_GITREPO=$GITREPO"
			echo "#define LIBRARY_GITBRANCH=$GITBRANCH"
			echo "#define LIBRARY_CACHEDIR=$CACHEDIR"
			echo "#define LIBRARY_DATADIR=$DATADIR"
			echo "#define LIBRARY_SVNLSOPTIONS=$SVNLSOPTIONS"
		elif [ "$FORMAT" == "csv" ]; then
			echo "name,value"
			echo "GITHUB,\"$GITHUB\""
			echo "GITHUBUSERCONTENT,\"$GITHUBUSERCONTENT\""
			echo "ORGANIZATION,\"$ORGANIZATION\""
			echo "GITUSER,\"$GITUSER\""
			echo "GITREPO,\"$GITREPO\""
			echo "GITBRANCH,\"$GITBRANCH\""
			echo "CACHEDIR,\"$CACHEDIR\""
			echo "DATADIR,\"$DATADIR\""
			echo "SVNLSOPTIONS,\"$SVNLSOPTIONS\""
		else
			echo "GITHUB=\"$GITHUB\""
			echo "GITHUBUSERCONTENT=\"$GITHUBUSERCONTENT\""
			echo "ORGANIZATION=\"$ORGANIZATION\""
			echo "GITUSER=\"$GITUSER\""
			echo "GITREPO=\"$GITREPO\""
			echo "GITBRANCH=\"$GITBRANCH\""
			echo "CACHEDIR=\"$CACHEDIR\""
			echo "DATADIR=\"$DATADIR\""
			echo "SVNLSOPTIONS=\"$SVNLSOPTIONS\""
		fi
	elif [ "$1" == "reset" ]; then
		rm -f $CONFIGFILE || error 1 "unable to reset $CONFIGFILE"
	elif [ "$1" == "set" -a $# == 3 ]; then
		echo "$2=\"$3\"" >> $CONFIGFILE
	elif [ "$1" == "get" -a $# == 2 ]; then
		if [ ! -r $CONFIGFILE ]; then
			value=$(config show | grep $2)
		else
			value=$(grep ^$2 $CONFIGFILE | tail -n 1)
		fi
		if [ -z "$value" ]; then
			value=$(config show | grep $2)
		fi
		echo $value | cut -f2 -d= | cut -f2 -d\"
	else
		echo "'$1' is an invalid config option"
		exit 1
	fi
}

# set the internal parameters
CACHEDIR="$DATADIR/.gridlabd-library"
GITPROJ="$GITHUB/$GITUSER/$GITREPO"
GITFILE="$GITHUBUSERCONTENT/$GITUSER/$GITREPO/$GITBRANCH"
COMMIT_ID=$(git ls-remote "$GITPROJ" | grep "$GITBRANCH" | head -n 1 | cut -f1)
INDEX=$CACHEDIR/$COMMIT_ID

function fetch_index()
{
	if [ ! -d $DATADIR ]; then
		mkdir -p $DATADIR || error 1 "unable to create $DATADIR"
	fi
	if [ ! -d $CACHEDIR ]; then
		mkdir -p $CACHEDIR || error 1 "unable to create $CACHEDIR"
	fi
	if [ ! -f $INDEX ]; then
		echo -n "Downloading ${GITBRANCH} index of $ORGANIZATION library archive from ${GITPROJ}... "
		svn $SVNLSOPTIONS ls $GITPROJ/branches/$GITBRANCH/$ORGANIZATION | grep '.glm$' > $INDEX || rm -f $INDEX
		echo $(wc -l $INDEX | sed -e 's/ \+/ /g;s/^ //' | cut -f1 -d' ') "library files found"
	fi
	if [ ! -f $INDEX ]; then
		error 1 "unable to fetch index"
	fi
}

#
# command line interface
#
function help()
{	
	if [ $# == 0 ]; then
		cat <<-END
			Syntax: gridlabd [options] library <command> [...]
			Options:
			  -f|--format <format> Change output format (e.g., json, glm, csv, default)
			Commands:
			  help                 Get the list of library subcommands
			  index <pattern>      Index of available library data matching <pattern> in archive
			  list <pattern>       List of available downloaded library data matching <pattern>
			  get <pattern>        Download library data matching <pattern> from archive 
			  delete <pattern>     Delete downloaded library data matching <pattern>
			  info <pattern>       Get information about library data in <pattern>
			  copy <name> [<file>] Copy the contents of the library <name> into <file>
			  clean                Clean up the local cache of the archive index
			  open <local>         Open an editing session of library in <local>
			  session <local> [<pattern>]  
			                       List current sessions in <local> (active session is first)
			  add <local> <pattern>
			                       Add files <pattern> to the <local> repository
			  save <local> ["<description>"]
			                       Save the <local> library changes
			  submit <local>       Submit the saved changes to the library
			  config show          Show the current configuration in environment format
			         reset         Reset the current configuration
			         set <N> <V>   Set configuration variable name <N> to value <V>
			         get <N> <V>   Get configuration variable name <N>
			END
	fi
}

function clean()
{
	rm -rf $CACHEDIR 
	if [ -d $CACHEDIR ]; then
		error 1 "unable to delete $CACHEDIR"
	fi
	fetch_index
}

function index()
{
	fetch_index
	if [ $# -eq 0 ]; then
		LIST=$(cat $INDEX)
	else
		LIST=$(grep "$1" $INDEX)
	fi
	if [ "$FORMAT" == "glm" ]; then
		echo "#define LIBRARY_INDEX=$LIST"
	elif [ "$FORMAT" == "csv" ]; then
		echo "filename,pathname"
		for f in $LIST; do
			echo "$f,\"$INDEX/$f\""
		done
	elif [ "$FORMAT" == "json" ]; then
		echo "{"
		first=yes
		for f in $LIST; do
			if [ "$first" == "no" ]; then
				echo ","
			else
				first=no
			fi
			echo -n "	\"$f\" : \"$INDEX/$f\""
		done
		echo ""
		echo "}"
	else
		for f in $LIST; do
			echo $f
		done
	fi
}

function list()
{
	fetch_index
	for f in $(export FORMAT=default; index $1); do
		if [ -f $DATADIR/$f ]; then
			echo $f
		fi
	done
}

function get()
{
	fetch_index
	for f in $(export FORMAT=default; index $1); do
		if [ ! -f $DATADIR/$f ]; then
			echo -n "Downloading $f... "
			(curl -s $GITFILE/$ORGANIZATION/$f > /tmp/$$ && mv /tmp/$$ $DATADIR/$f && echo "done") || (rm -f /tmp/$$; error 1 "unable to download $f")
		else
			echo -n "Refreshing $f... "
			(curl -s $GITFILE/$ORGANIZATION/$f -z $DATADIR/$f > /tmp/$$ && mv /tmp/$$ $DATADIR/$f && echo "done") || (rm -f /tmp/$$; error 1 "unable to refresh $f")
		fi
	done
}

function info()
{
	fetch_index
	first="yes"
	KEYWORDS="Name,Version,Author,Description,Module"
	if [ "$FORMAT" == "json" ]; then
		echo "["
		for f in $(export FORMAT=default; index $1); do
			if [ -f $DATADIR/$f ]; then
				if [ "$first" == "yes" ]; then
					first="no"
				else
					echo ","
				fi
				echo "	{"
				echo "		\"datadir\" : \"$DATADIR/$f\","
				for KEY in ${KEYWORDS//,/ }; do
					VALUE=`grep "^#define LIBRARY_$KEY=" $DATADIR/$f | cut -f2 -d=`
					echo "		\"$KEY\" : ${VALUE:-None},"
				done
				echo "		\"organization\" : \"$ORGANIZATION\""
				echo -n "	}"
			fi
		done
		if [ "$first" == "no" ]; then
			echo ""
		fi
		echo "]"
	elif [ "$FORMAT" == "glm" ]; then
		echo $(index $1)
	else
		for f in $(export FORMAT=default; index $1); do
			if [ -f $DATADIR/$f ]; then
				if [ "$first" == "yes" ]; then
					echo "Filepath,Organization,$KEYWORDS"
					first="no"
				fi
				echo -n "$DATADIR/$f,$ORGANIZATION"
				for KEY in ${KEYWORDS//,/ }; do
					VALUE=`grep "^#define LIBRARY_$KEY=" $DATADIR/$f | cut -f2 -d=`
					echo -n ",${VALUE:-\"\"}"
				done
				echo ""
			fi
		done
	fi
}

function copy()
{
	fetch_index
	if [ $# -eq 0 ]; then
		error 1 "missing library name"
	fi
	FROM=$DATADIR/$1
	if [ ! -r $FROM ]; then
		error 2 "$FROM not found"
	fi
	TO=$1
	if [ $# -gt 1 ]; then
		TO=$2
	fi
	if [ -f $TO ]; then
		error 3 "$TO exists"
	fi
	if [ $# -eq 1 ]; then
		cp $FROM $TO || exit 4
		exit 0
	fi
	EXT=${TO##*.}
	# convert automatically (TODO such that only objects are saved)
	TMP=/tmp/tmp-$$
	sed -e '1,$s/^#define LIBRARY_Module="\(.*\)"/module \1;/' < $FROM > $TMP.glm || exit 5
	gridlabd -D filesave_options=OBJECTS -D glm_save_options=MINIMAL -C $TMP.glm -o $TMP.$EXT || exit 6
	cat $TMP.$EXT > $TO || exit 7
	rm -f $TMP.*
}

function delete()
{
	fetch_index
	for f in $(export FORMAT=default; index $1); do
		if [ -f $DATADIR/$f ]; then
			rm -f $DATADIR/$f || error 1 "unable to delete $f"
		fi
	done

}

function open()
{
	if [ $# -eq 0 ]; then
		error 1 "missing local library name"
	fi
	LIBDIR=$1
	if [ -d $LIBDIR -a -d $LIBDIR/.git ]; then
		cd $LIBDIR
		git pull -q
	else
		git clone -q "$GITPROJ" $LIBDIR
		cd $LIBDIR
	fi
	if [ ! -d .git ]; then
		error 3 "$LIBDIR is not a valid git repository (missing $LIDIR/.git)"
	fi
	EMAIL=$(git config --get user.email)
	if [ -z "$EMAIL" ]; then
		error 3 "user.email not set for repository $LIBDIR"
	fi
	SESSION=$USER/$(date '+%y%m%d/%H%M%S')
	git checkout -q -b $SESSION
}

function session()
{
	if [ $# -eq 0 ]; then
		error 1 "missing local library name"
	fi
	LIBDIR=$1
	if [ ! -d $LIBDIR -o ! -d $LIBDIR/.git ]; then
		error 2 "$LIBDIR is not a valid local library"
	fi
	if [ $# -eq 1 ]; then
		git branch | sort -r | cut -c3- | grep "^$USER/"
	else
		git checkout -q $2
	fi
}

function save()
{
	if [ $# -eq 0 ]; then
		error 1 "missing local library name"
	fi
	LIBDIR=$1
	if [ ! -d $LIBDIR/.git ]; then
		error 2 "$LIBDIR is not a valid git repository (missing $LIDIR/.git)"
	fi
	cd $LIBDIR
	if [ $# -eq 1 ]; then
		$MESSAGE="Save by $USER on $DATE"
	else
		$MESSAGE="$2"
	fi
	git commit -a -m "$MESSAGE"
	git push
	SESSION=$(git branch | grep '^*' | cut -f2 -d' ')
	file=$GITPROJ/compare/master...$SESSION
	eval $OPENURLCMD
}

function add()
{
	if [ $# -eq 0 ]; then
		error 1 "missing local library name"
	fi
	LIBDIR=$1
	if [ $# -eq 1 ]; then
		error 2 "missing file name"
	fi
	cd $LIBDIR
	git add -q $2
}

if [ "$1" == "-f" -o "$1" == "--format" ]; then
	FORMAT=$2
	shift 2
fi

if [ $# == 0 -o "$1" == "help" ]; then
	help $2
	exit 0
elif [ "$(type -t $1)" = "function" ]; then
	$*
	exit 0
else
	error 1 "'$1' is not a valid library command"
fi
