#!/usr/bin/env bash

source dz-common

# An array to store Workshop items. Each element contains the mod's ID, name, and state (active or not).
WORKSHOP_DIR="/mods/${release_client_appid}"
if [ ! -d ${WORKSHOP_DIR} ]
then
  mkdir -p ${WORKSHOP_DIR}
fi

workshoplist=""

# Functions

# Usage
usage(){
  echo -e "
${red}Bad option or arguments! ${yellow}${*}${default}

Usage: ${green}$(basename $0)${yellow} option [ arg1 [ arg2 ] ]

Options and arguments:

  a|add id - Add a DayZ Workshop item by id. Added items become active by default
  i|install - Install the DayZ server files
  g|login - Login to Steam.
  m|modupdate - Update the mod files
  p|map id - Install a mod's mpmissions files by id. (Presumes template exists)
  r|remove id - Remove all files and directories of a Workshop item by id
  l|s|status - Shows Steam login status, if base files are installed, installed mods
  u|update - Update the server files
  x|xml id - Get and normalize XML files from a mod's template by id (Presumes template exists)
${default}"
  exit 1
}

# Manage the mod symlink
symlink(){
	W=${1}
	ID=${2}
	NAME=${3}
	if [ ! -L "${SERVER_FILES}/@${NAME}" ] && [[ ${W} = 1 ]]
	then
		ln -sv ${WORKSHOP_DIR}/${ID} "${SERVER_FILES}/@${NAME}"
	elif [[ "${W}" = "0" ]]
	then
		rm -vf "${SERVER_FILES}/@${NAME}"
	fi
}

installxml(){
  ID=${1}
  # Going to have to maintain a matrix of file names -> root node -> child node permutations
  for i in "CFGEVENTGROUPS:eventgroupdef:group" "CFGEVENTSPAWNS:eventposdef:event" "CFGSPAWNABLETYPES:spawnabletypes:type" "EVENTS:events:event" "TYPES:types:type"
  do
    var=$(echo ${i} | cut -d: -f1)
    CHECK=$(echo ${i} | cut -d: -f2)
    if [ -f "${WORKSHOP_DIR}/${ID}/${var,,}.xml" ]
    then
      echo "Normalizing ${WORKSHOP_DIR}/${ID}/${var,,}.xml..."
      cp ${WORKSHOP_DIR}/${ID}/${var,,}.xml /tmp/x
      # Quirks
      # Some cfgeventspanws.xml files have <events> instead of <eventposdef>. Let's just try to fix that first.
      if [[ ${var} = "CFGEVENTSPAWNS" ]]
			then
				if grep -q '<events>' /tmp/x
				then
					echo "  - (Quirk) has <events> instead of <eventposdef>. fixing..."
					xmlstarlet ed -L -r "events" -v "eventposdef" /tmp/x
				fi
			fi
      if ! grep -q '<'${CHECK}'>' /tmp/x
      then
        echo "  - has no root node <${CHECK}>. fixing..."
        echo '<'${CHECK}'>' > /tmp/y
        cat /tmp/x >> /tmp/y
        echo '</'${CHECK}'>' >> /tmp/y
        xmlstarlet fo /tmp/y > /tmp/x
      fi
      if ! grep -q '<?xml' /tmp/x
      then
        echo "  - has no XML node, fixing..."
        xmlstarlet fo /tmp/x > /tmp/y
        mv /tmp/y /tmp/x
      fi
      xmllint --noout /tmp/x && (
        # Keep the normalized version in the /mods directory
        cp /tmp/x ${WORKSHOP_DIR}/${ID}/${var,,}.xml
        echo -e "${green}${WORKSHOP_DIR}/${ID}/${var,,}.xml passes XML lint test!${default}"
      ) || (
        echo -e "${yellow}The final ${WORKSHOP_DIR}/${ID}/${var,,}.xml does not pass XML lint test! IT WAS NOT COPIED!${default}"
      )
    fi
  done
  exit 0
}

# Add a mod
add(){
	if [ -d "${WORKSHOP_DIR}/${1}" ]
	then
		echo -e "${yellow}Warning: The mod directory ${WORKSHOP_DIR}/${1} already exists!${default}"
		MODNAME=$(get_mod_name ${1})
	fi
	if [ -L "${SERVER_FILES}/@${MODNAME}" ]
	then
		echo -e "${yellow}Warning: The mod symlink ${SERVER_FILES}/@${MODNAME} already exists!${default}"
	fi
	echo "Adding mod id ${1}"
	dologin
	${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" +workshop_download_item "${release_client_appid}" "${1}" +quit
	# Make sure the install succeeded
	if [ ! -d "${WORKSHOP_DIR}/${1}" ]
	then
		echo -e "${red}Mod installation failed: The mod directory ${WORKSHOP_DIR}/${1} was not created!${default}"
		echo "Installation failed! See above (You probably need to use a real Steam login)"
		return
	fi
	# Get the name of the newly added mod
	MODNAME=$(get_mod_name ${1})
	symlink 1 ${1} "${MODNAME}"
	echo -e "Mod id ${1} - ${green}${MODNAME}${default} - added"
	xml ${ID}
	map ${ID}
}

# Remove a mod
remove(){
	DIR="${WORKSHOP_DIR}/${1:?}"
	if [ -d "${DIR}" ]
	then
		MODNAME=$(get_mod_name ${1})
		echo "Removing directory ${DIR}"
		rm -rf "${DIR}"
	else
		echo "Directory ${DIR} doesn't exist?"
	fi
	if [ -L "${SERVER_FILES}/@${MODNAME}" ]
	then
		echo "Removing symlink ${SERVER_FILES}/@${MODNAME}"
		rm -f "${SERVER_FILES}/@${MODNAME}"
	else
		echo "Symlink ${SERVER_FILES}/@${MODNAME} doesn't exist?"
	fi
	echo -e "Mod id ${1} - ${red}${MODNAME}${default} - removed"
}

# Handle the Steam login information.
login(){
	if [ -f "${STEAM_LOGIN}" ]
	then
		if prompt_yn "The steam login is already set. Reset it?"
		then
			rm -f "${STEAM_LOGIN}"
		else
			echo "Not reset."
			exit 0
		fi
	fi
	if [ ! -f "${STEAM_LOGIN}" ]
	then
		echo "Setting up Steam credentials"
		echo -n "Steam Username (anonymous): "
		read steamlogin
		if [[ "${steamlogin}" = "" ]]
		then
			echo "Steam login set to 'anonymous'"
			steamlogin="anonymous"
		fi
		echo "steamlogin=${steamlogin}" > "${STEAM_LOGIN}"
		${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" +quit
	fi
}

# "Perform" the Steam login. This just sources the file with the Steam login name.
dologin(){
	if [ -f "${STEAM_LOGIN}" ]
	then
		source "${STEAM_LOGIN}"
	else
		echo "No cached Steam credentials. Please configure this now: "
		login
	fi
}

# Perform the installation of the server files.
install(){
	if [ ! -f "${SERVER_INSTALL_FILE}" ] || [[ ${1} = "force" ]]
	then
		printf "[ ${yellow}DayZ${default} ] Downloading DayZ Server-Files!\n"
		dologin
		${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" +app_update "${release_server_appid}" validate +quit
		# This installs the mpmissions for charnarusplus and enoch (AKA Livonia) from github. The game once allowed the full server
		# to be downloaded, but now you get the server without any mpmissions. This is a workaround.
		echo "Installing mpmissions for ChernarusPlus and Livonia from github..."
		map default
	else
		printf "[ ${lightblue}DayZ${default} ] The server is already installed.\n"
	fi
}

# Update the server files.
update(){
	dologin
	appmanifestfile=${SERVER_FILES}/steamapps/appmanifest_"${release_server_appid}".acf
	printf "[ ... ] Checking for update:"
	# gets currentbuild
	currentbuild=$(grep buildid "${appmanifestfile}" | tr '[:blank:]"' ' ' | tr -s ' ' | cut -d \  -f3)
	# Removes appinfo.vdf as a fix for not always getting up to date version info from SteamCMD
	if [ -f "${HOME}/Steam/appcache/appinfo.vdf" ]
	then
		rm -f "${HOME}/Steam/appcache/appinfo.vdf"
	fi
	# check for new build
	availablebuild=$(${STEAMCMD} +login "${steamlogin}" +app_info_update 1 +app_info_print "${release_server_appid}" +quit | \
		sed -n '/branch/,$p' | grep -m 1 buildid | tr -cd '[:digit:]')
	if [ -z "${availablebuild}" ]
	then
		printf "\r[ ${red}FAIL${default} ] Checking for update:\n"
		printf "\r[ ${red}FAIL${default} ] Checking for update:: Not returning version info\n"
		exit
	else
		printf "\r[ ${green}OK${default} ] Checking for update:"
	fi
	# compare builds
	if [ "${currentbuild}" != "${availablebuild}" ] || [[ ${1} = "force" ]]
	then
		printf "\r[ ${green}OK${default} ] Checking for update:: Update available\n"
		printf "Update available:\n"
		printf "\tCurrent build: ${red}${currentbuild}${default}\n"
		printf "\tAvailable build: ${green}${availablebuild}${default}\n"
		printf "\thttps://steamdb.info/app/${release_server_appid}/\n"
		printf "\nApplying update"
		# run update
		dologin
		${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" +app_update "${release_server_appid}" validate +quit
		modupdate
	else
		printf "\r[ ${green}OK${default} ] Checking for update:: No update available\n"
		printf "\nNo update available:\n"
		printf "\tCurrent version: ${green}${currentbuild}${default}\n"
		printf "\tAvailable version: ${green}${availablebuild}${default}\n"
		printf "\thttps://steamdb.info/app/${release_server_appid}/\n\n"
	fi
}

# Update mods
modupdate(){
	echo "Updating mods..."
	dologin
	get_mods
	${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" ${workshoplist} +quit
	# Updated files come in with mixed cases. Fix that.
	echo "done"
	echo
}

# Display the status of everything
status(){
	INSTALLED="${NO}"
	LOGGED_IN="${NO}"

	# DayZ Server files installation
	if [ -f "${SERVER_INSTALL_FILE}" ]
	then
		INSTALLED="${YES}"
		if [[ ${release_client_appid} = "221100" ]]
		then
			RELEASE="Stable"
		else
			RELEASE="Experimental"
		fi
		VERSION="$(strings /serverfiles/DayZServer | grep -P "DayZ \d\.\d+\.\d+" | cut -c6-) - ${RELEASE}"
	fi
	# Logged into Steam
	if [ -f "${STEAM_LOGIN}" ]
	then
		LOGGED_IN="${YES}"
		if grep -q anonymous "${STEAM_LOGIN}"
		then
			ANONYMOUS="${yellow}(as anonymous)${default}"
		else
			ANONYMOUS="${green}(not anonymous)${default}"
		fi
	fi
	echo -ne "
Logged in to Steam: ${LOGGED_IN} ${ANONYMOUS}
Server files installed: ${INSTALLED}"
	if [[ "${INSTALLED}" = "${NO}" ]]
	then
		echo
		echo
		exit 0
	fi
	# Version of DayZ Server files
	echo -ne "
Version: ${VERSION}"

	# Mods
	echo -ne "
Mods: "
	MODS=$(list)
	if [[ ${MODS} == "" ]]
	then
		echo -ne "${red}none${default}"
	fi
	echo -e "${MODS}"
}

map(){
	# Install map mpmissions for mods that have them. Presumes a map.env was created for the mod, with the required metadata (git URL, etc.)
	TERM="map"
  if [[ "${1}" =~ ^[0-9]+$ ]]
  then
  	TERM="mod id"
  fi
	if [ -f "${FILES}/mods/${1}/map.env" ]
	then
		echo "Installing mpmissions files for ${TERM} ${1}..."
		source ${FILES}/mods/${1}/map.env
		${FILES}/bin/map.sh ${1} install
	fi
}

mod_install(){
	if [ -f ${FILES}/mods/${1}/${2}.sh ]
	then
		echo "An ${2}.sh was found for mod id ${1}. Running..."
		${FILES}/mods/${1}/${2}.sh
	fi
	# A generic map install script. Presumes a git repo as the source
}

# "Manage" XML files.
xml(){
  /files/bin/xml.sh ${1}
  installxml ${1}
}

# Capture the first argument and shift it off so we can pass $@ to every function
C=${1}
shift || {
	usage
}

case "${C}" in
	a|add)
		add "${@}"
		;;
	i|install)
		install "${@}"
		;;
	g|login)
		login "${@}"
		;;
	m|modupdate)
		modupdate "${@}"
		;;
	r|remove)
		remove "${@}"
		;;
	l|s|status)
		status "${@}"
		;;
	p|map)
		map "${@}"
		;;
	u|update)
		update "${@}"
		;;
	x|xml)
		xml "${@}"
		;;
	*)
		usage "$*"
	;;
esac
