#!/bin/bash

### BEGIN INIT INFO
# Provides:	  msm
# Required-Start: $local_fs $remote_fs
# Required-Stop:  $local_fs $remote_fs
# Should-Start:	  $network
# Should-Stop:	  $network
# Default-Start:  2 3 4 5
# Default-Stop:	  0 1 6
# Short-Description:	
# Description:	  
### END INIT INFO


# See http://www.debian.org/doc/debian-policy/ch-opersys.html#s-sysvinit for
# more information on debain init.d scripts, which may help you understand
# this script.


source "msm.config"

WHITELIST="white-list.txt"
BANNED_IPS="banned-ips.txt"
BANNED_PLAYERS="banned-players.txt"

JARGROUP_TARGET="target.txt"
JARGROUP_DOWNLOAD_DIR="downloads"


### Utility Functions

# Esecutes the command "$1" as SERVER_USER
as_user() {
	if [ $(whoami) == $SERVER_USER ] ; then
		bash -c "$1"
	else
		su - $SERVER_USER -s /bin/bash -c "$1"
	fi
}

as_user_stderr() {
	as_user "$1" 2>&1 1>/dev/null
}

echoerr() {
	echo "$@" 1>&2
}

is_valid_name() {
	local valid="^[a-zA-Z0-9\_\-]+$"
	local invalid="^server|jargroup|start|stop|restart$"
	
	if [[ "$1" =~ $valid ]]; then
		if [[ "$1" =~ $invalid ]]; then
			echoerr "Invalid name \"$1\": A name may not be any of the following reserved worlds \"start\", \"stop\", \"restart\", \"server\" or \"jargroup\"."
			exit 1
		else
			exit 0
		fi
	else
		echoerr "Invalid name \"$1\": A name may only contain letters, numbers, dashes and unscores."
		exit 1
	fi
}

get_latest_file() {
	local best_time=0
	local best_file=""
	
	for file in "$1/*"; do
		# Remove the path, leaving just the file name
		local name=$(basename "$file")
		
		# Get the time in seconds since 1970 from file name
		local seconds=$(date -d "${name:0:10}" "+%s" 2> /dev/null)
		
		# If that is newer than the current best, override variables
		if [[ $seconds > $best_time ]]; then
			best_time=$seconds
			best_file=$file
		fi
	done
	
	echo $best_file
}


### Manager Functions

manager_create_server() {
	if is_valid_name "$1"; then
		if [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then
			echo "A server with that name already exists."
			exit 1
		else
			printf "Creating server directory... "
			as_user "mkdir -p '$SERVER_STORAGE_PATH/$1'"
			as_user "touch '$SERVER_STORAGE_PATH/$1/$WHITELIST'"
			as_user "touch '$SERVER_STORAGE_PATH/$1/$BANNED_IPS'"
			as_user "touch '$SERVER_STORAGE_PATH/$1/$BANNED_PLAYERS'"
			echo "Done."
		fi
	else
		exit 1
	fi
}

manager_delete_server() {
	if is_valid_name "$1" && [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then
		printf "Are you sure you want to delete this server and its worlds (note: backups are preserved) [y/N]: "

		read answer
		if [[ "$answer" =~ ^y|Y|yes$ ]]; then
			# TODO: stop the server if running first
			as_user "rm -r '$SERVER_STORAGE_PATH/$1'"
			echo "Server deleted."
		else
			echo "Server was NOT deleted."
		fi
	else
		if [[ ! -e "$SERVER_STORAGE_PATH/$1" ]]; then
			echo "There is no server with the name \"$1\"."
		fi
		exit 1
	fi
}

manager_rename_server() {
	if is_valid_name "$1" && [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then
		# If the server name is valid,
		# and there is no other server with the name $1
		
		# TODO: Check that the server is not running first, exit if it is
		
		if is_valid_name "$2"; then
			if [[ -e "$SERVER_STORAGE_PATH/$2" ]]; then
				echo "Could not be renamed, there is already a server with the name \"$2\"."
				exit 1
			else
				as_user "mv '$SERVER_STORAGE_PATH/$1' '$SERVER_STORAGE_PATH/$2'"
				echo "Renamed server \"$1\" to \"$2\"."
			fi
		else
			echo "Invalid name: The new server name may only contain letters, numbers, a dash or an underscore."
		fi
	else
		if [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then
			echo "There is no server with the name \"$1\"."
		fi
		exit 1
	fi
}

jargroup_create() {
	if is_valid_name "$1"; then
		if [[ -e "$JAR_STORAGE_PATH/$1" ]]; then
			echo "A jar group with that name already exists."
			exit 2
		else
			printf "Creating jar group... "
			
			local error=$(as_user_stderr "mkdir -p '$JAR_STORAGE_PATH/$1'")
			if [[ "$error" != "" ]]; then
				echo "Failed."
				echo "Reason: $error"
				exit 1
			fi
			
			error=$(as_user "echo \"$2\" > '$JAR_STORAGE_PATH/$1/$JARGROUP_TARGET'")
			if [[ "$error" != "" ]]; then
				echo "Failed."
				echo "Reason: $error"
				exit 1
			fi
			
			echo "Done."
			
			# Download the latest version now
			jargroup_getlatest "$1"
			
		fi
	else
		exit 1
	fi
}

jargroup_getlatest() {
	if is_valid_name "$1" && [[ -e "$JAR_STORAGE_PATH/$1" ]]; then
		if [[ -e "$JAR_STORAGE_PATH/$1/$JARGROUP_TARGET" ]]; then
			printf "Downloading latest version... "
			
			# Try and make 
			local error=$(as_user_stderr "mkdir -p '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR'")
			if [[ $error != "" ]]; then
				echo "Failed."
				echo "Reason: $error"
				exit 1
			fi
			
			as_user "wget --quiet --input-file='$JAR_STORAGE_PATH/$1/$JARGROUP_TARGET' --directory-prefix='$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR' --no-check-certificate"
			echo "Done."
			
			local num_files=$(as_user "ls -1 '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR' | wc -l")
			
			if [[ $num_files == 1 ]]; then
				# There was 1 file downloaded
				
				local file_name=$(ls -1 "$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR")
				local new_name="$(date +%F)-$file_name"
				local most_recent_jar=$(get_latest_file "$JAR_STORAGE_PATH/$1")
				
				if [[ ! -e "$most_recent_jar" ]] || ! diff "$most_recent_jar" "$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR/$file_name"; then
					# There is not a previous version to do a comparison against, or
					# The previous version is different:
					# Add it to the group
					
					[[ -e "$most_recent_jar" ]]
					local was_previous=$?
					
					as_user "mv '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR/$file_name' '$JAR_STORAGE_PATH/$1/$new_name'"
					
					if [[ $was_previous == 0 ]]; then
						echo "Downloaded version was different to previous latest. Saved as \"$1/$new_name\"."
					else
						echo "Saved as \"$JAR_STORAGE_PATH/$1/$new_name\"."
					fi
				else
					echo "Existing version \"$JAR_STORAGE_PATH/$1/$new_name\" was already up to date."
				fi
				
			elif [[ $num_files == 0 ]]; then
				# No file was downloaded
				echo "Failed. No files were downloaded."
			else
				# Multiple files were
				echo "Error. URL downloads multiple files."
			fi
			
			# Clean up the temp download folder
			as_user "rm -r '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR'"
		else
			echo "Target URL not found."
		fi
	else
		if [[ -e "$JAR_STORAGE_PATH/$1" ]]; then
			echo "There is no jar group with the name \"$1\"."
		fi
	fi
}

jargroup_delete() {
	if is_valid_name "$1" && [[ -e "$JAR_STORAGE_PATH/$1" ]]; then
		printf "Are you sure you want to delete this jar group [y/N]: "
		
		read answer
		if [[ "$answer" =~ ^y|Y|yes$ ]]; then
			as_user "rm -r $JAR_STORAGE_PATH/$1"
			echo "Jar group deleted."
		else
			echo "Jar group was NOT deleted."
		fi
	else
		if [[ -e "$JAR_STORAGE_PATH/$1" ]]; then
			echo "There is no jar group with the name \"$1\"."
		fi
	fi
}


### Script Options

case "$1" in
	start)
		# Required start option, for debian init.d scripts
		;;
	stop)
		# Required stop option, for debian init.d scripts
		;;
	restart)
		# Required restart option, for debian init.d scripts
		;;
	server)
		case "$2" in
			list)
				;;
			create)
				if [ -z "$3" ]; then
					# If a server name is not provided
					echo "Invalid command."
				else
					# Create a new server
					manager_create_server "$3"
				fi
				;;
			delete)
				if [ -z "$3" ]; then
					# If a server name is not provided
					echo "Invalid command."
				else
					# Delete an existing server, with confirmation
					manager_delete_server "$3"
				fi
				;;
			rename)
				if [ -z "$3" ] || [ -z "$4" ]; then
					# If a server name is not provided
					echo "Invalid command."
				else
					# Rename an existing server
					manager_rename_server "$3" "$4"
				fi
				;;
			*)
				echo "Invalid command."
				;;
		esac
		;;
	jargroup)
		case "$2" in
			list)
				;;
			create)
				jargroup_create "$4" "$5"
				;;
			delete)
				jargroup_delete "$4"
				;;
			rename)
				;;
			changetarget)
				jargroup_settarget "$4" "$5"
				;;
			getlatest)
				jargroup_getlatest "$4"
				;;
			*)
				echo "Invalid command."
				;;
		esac
		;;
	help)
		echo -e "Usage: $0 command:"
		echo -e
		echo -e "--Setup Commands------------------------------------------------"
		echo -e "  server list \t\t\t\t\tList servers"
		echo -e "  server create <name> \t\t\t\tCreates a new Minecraft server"
		echo -e "  server delete <name> \t\t\t\tDeletes an existing Minecraft server"
		echo -e "  server rename <name> <new-name> \t\tRenames an existing Minecraft server"
		echo -e
		echo -e "--Server Mangement Commands-------------------------------------"
		echo -e "  <server> start \t\t\t\tStarts a server"
		echo -e "  <server> stop [now] \t\t\t\tStops a server after warning players, or right now"
		echo -e "  <server> restart [now] \t\t\tRestarts a server after warning players, or right now"
		echo -e "  <server> worlds \t\t\t\tLists the worlds a server has"
		echo -e "  <server> worlds load \t\t\t\tCreates links to wolrds in storage for a server"
		echo -e "  <server> worlds todisk \t\t\tSynchronises any \"in RAM\" worlds to disk a server has"
		echo -e "  <server> worlds toram \t\t\tSynchronises any RAM enabled worlds to RAM a server has"
		echo -e "  <server> worlds backup \t\t\tMakes a backup of all worlds a server has"
		echo -e "  <server> worlds ram <world> \t\t\tToggles a world's \"in RAM\" status"
		echo -e "  <server> backup \t\t\t\tMakes a backup of an entire server directory"
		echo -e "  <server> logroll \t\t\t\tMove a server log to a gziped archive, to reduce lag"
		echo -e "  <server> connected \t\t\t\tList a servers connected players"
		echo -e "  <server> status \t\t\t\tShow the running/stopped status of a server"
		echo -e
		echo -e "--Server Pass Through Commands----------------------------------"
		echo -e "  <server> wl on|off <player> \t\t\tEnabled/disable server whitelist check"
		echo -e "  <server> wl add|remove <player> \t\tAdd/remove a player to/from a server's whitelist"
		echo -e "  <server> wl list \t\t\t\tList the players whitelisted for a server"
		echo -e "  <server> bl player add|remove <player> \tBan/pardon a player from/for a server"
		echo -e "  <server> bl ip add|remove <ip address> \tBan/pardon an IP address from/for a server"
		echo -e "  <server> bl list \t\t\t\tLists the banned players and IP address for a server"
		echo -e "  <server> op add|remove <player> \t\tAdd/remove operator status for a player on a server"
		echo -e "  <server> gm survival|creative <player> \tChange the game mode for a player on a server"
		echo -e "  <server> kick <player> \t\t\tForcibly disconnect a player from a server"
		echo -e "  <server> say <message> \t\t\tBroadcast a (pink) message to all players on a server"
		echo -e "  <server> time set|add <number> \t\tSet/increment time on a server (0-24000)"
		echo -e "  <server> toggledownfall \t\t\tToggles rain and snow on a server"
		echo -e "  <server> save on|off \t\t\t\tEnable/disable writing world changes to file"
		echo -e "  <server> save all \t\t\t\tForce the writing of all non-saved world changes to file"
		echo -e "  <server> cmd <command> \t\t\tSend a command string to the server and return"
		echo -e "  <server> cmdlog <command> \t\t\tSame as 'cmd' but shows log output afterwards (Ctrl+C to exit)"
		echo -e
		echo -e "--Jar Commands--------------------------------------------------"
		echo -e "  jargroup list \t\t\t\tList the stored jar files."
		echo -e "  jargroup create <name> <download-url> \tCreate a new jar group, with a URL for new downloads"
		echo -e "  jargroup delete <name> \t\t\tDelete a jar group"
		echo -e "  jargroup rename <name> <new-name> \t\tRename a jar group"
		echo -e "  jargroup changeurl <name> <download-url> \tChange the download URL for a jar group"
		echo -e "  jargroup getlatest <name> \t\t\tDownload the latest jar file for a jar group"
		echo -e
		echo -e "--Global Commands-----------------------------------------------"
		echo -e "  start \t\t\t\t\tStarts all active servers"
		echo -e "  stop \t\t\t\t\t\tStops all running servers"
		echo -e "  restart \t\t\t\t\tRestarts all active servers"
		;;
	"")
		echo "No such command see: $0 help"
		;;
	*)
		# TODO: Check first if $1 is an existing server name
		
		if is_valid_name "$1" && [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then
			case "$2" in
				start)
					;;
				stop)
					;;
				restart)
					;;
				worlds)
					;;
				backup)
					;;
				logroll)
					;;
				connected)
					;;
				status)
					;;
				whitelist|wl)
					;;
				blacklist|bl)
					;;
				operator|op)
					;;
				gamemode|gm)
					;;
				kick)
					;;
				say)
					;;
				"time")
					;;
				toggledownfall)
					;;
				save)
					;;
				cmd)
					;;
				cmdlog)
					;;
			esac
		else
			echo "No server with that name."
		fi
		;;
esac

exit 0