#!/usr/bin/bash

function pecho() {
	if [[ "$1" = "debug" ]] && [[ "${PORTABLE_LOGGING}" = "debug" ]]; then
		echo "[Debug]		$2"
	elif [[ "$1" = "info" ]] && [[ ! "${PORTABLE_LOGGING}" = "warn" ]]; then
		echo "[Info]		$2"
	elif [[ "$1" = "warn" ]]; then
		echo "[Warn]		$2"
	elif [[ "$1" = "crit" ]]; then
		echo "[Critical]	$2"
	fi
}

function printHelp() {
	echo "This is Portable packer, a tool to build sandboxed package"
	echo "Visit https://github.com/Kraftland/portable for documentation and information."
	echo "Supported arguments:"
	echo "	-v	-	-	-> Verbose output"
	echo "	--distro [distro name]	-> Specify the distribution."
	echo "	--mode [copy <[pkg]>]	-> Modes of operation"
	echo "	--hash [true / false]	-> Enables hashing of configuration file. Currently has no effect. (optional)"
	echo "	--config [path]	-	-> Specify the configuration source for sandbox"
	echo "	--desktop-file [path]	-> Specify the desktop file path for sandbox"
	echo "	--dbus-activation	-> Enables the activation from D-Bus (optional)"
	echo "Exit codes:"
	echo "	1	-	-	-> Syntax / argument error"
	echo "	110	-	-	-> Arch specific error"
	echo "	200	-	-	-> Configuration error"
	echo "	201	-	-	-> Non-existent package"
	exit 0
}

function busCheck() {
	local busOwn="${appID}"
	if [[ "${busOwn}" = org.mpris.MediaPlayer2$ ]]; then
		pecho crit "appID invalid: prohibited to own entire org.mpris.MediaPlayer2"
		exit 200
	elif [[ "${busOwn}" =~ org.freedesktop.impl.* ]]; then
		pecho crit "appID invalid: sandbox escape not allowed"
		exit 200
	elif [[ "${busOwn}" =~ org.gtk.vfs.* ]]; then
		pecho crit "appID invalid: full filesystem access not allowed"
		exit 200
	fi
}

function cmdlineDispatcher() {
	declare -i argumentCount=0
	while true; do
		if [[ $1 = "-v" ]]; then
			export PORTABLE_LOGGING=debug
			pecho debug "Enabled verbose logging"
			declare -g installVerbose="v"
		elif [[ $1 = "--distro" ]]; then
			pecho debug "Resolving distribution..."
			shift
			if [[ $1 =~ ^arch$|^archlinux$|^Arch ]]; then
				pecho info "Distribution set to \"Arch Linux\""
				declare -g _distribution="archlinux"
			else
				pecho crit "Unrecognized distribution: \"${1}\""
				exit 1
			fi
		elif [[ $1 = "--config" ]]; then
			pecho debug "Verifying configuration..."
			shift
			declare -r configPath="$(realpath --quiet $1)"
			if [[ $? -eq 0 ]]; then
				if [[ ! -r "${configPath}" || ! -f "${configPath}" ]]; then
					pecho crit "Failed reading configuration: expecting PATH to file, found \"$1\""
					exit 200
				fi
			else
				pecho crit "Failed reading configuration: "$(realpath $1)""
				exit 200
			fi
			declare -g configPath="${configPath}"
		elif [[ $1 = "--desktop-file" ]]; then
			pecho debug "Verifying desktop file..."
			shift
			declare -r desktopPath="$(realpath --quiet $1)"
			if [[ $? -eq 0 ]]; then
				if [[ ! -r "${desktopPath}" || ! -f "${desktopPath}" ]]; then
					pecho crit "Failed reading desktop file: expecting PATH to file, found \"$1\""
					exit 200
				fi
			else
				pecho crit "Failed reading desktop file: "$(realpath $1)""
				exit 200
			fi
			declare -g desktopPath="${desktopPath}"
		elif [[ $1 = "--mode" ]]; then
			shift
			if [[ $1 = "copy" ]]; then
				declare -g packerMode="$1"
				shift
				if [[ -n "${1}" ]]; then
					declare -g archPackage="$1"
				else
					pecho crit "Specify package name after --mode copy!"
					exit 1
				fi
				pecho debug "Mode set to: Copy from existing package: $1"
			fi
		elif [[ $1 = "--help" ]]; then
			pecho debug "Printing help on explicit request"
			printHelp
		elif [[ -z $* ]]; then
			declare trailS="s"
			if [[ "${argumentCount}" -eq 1 ]]; then
				unset trailS
			fi
			pecho debug "Resolution of command line arguments finished with ${argumentCount} argument${trailS}."
			if [[ "${argumentCount}" -eq 0 ]]; then
				printHelp
			fi
			break
		else
			pecho warn "Unrecognized option: $1"
			argumentCount+=-1
		fi
		argumentCount+=1
		shift
	done
}

function configCheck() {
	pecho debug "Processing configuration..."
	declare -i configLength=$(wc -l < "${1}")
	configLength=$((${configLength} + 1))
	pecho debug "Calculated configuration length: ${configLength}"
	declare -i lineCount=1
	while true; do
		if [[ "${lineCount}" -gt "${configLength}" ]]; then
			pecho debug "Finished parsing of configuration file"
			break
		fi
		declare lineContent
		lineContent="$(sed -n "${lineCount}p" "${1}")"
		pecho debug "Processing configuration @${lineCount}"
		if [[ "${lineContent}" =~ ^# ]]; then
			pecho debug "	=> Skipping comments"
		elif [[ "${lineContent}" =~ .*=.* ]]; then
			pecho debug "	=> KEY=VAL discovered: ${lineContent}"
			if [[ "${lineContent}" =~ .*=.*=.* ]]; then
				pecho crit "	=> Rejecting malformed configuration file! Expected KEY=VAL pair. found multiple \"=\" operators"
				exit 200
			fi
		elif [[ -z "${lineContent}" ]]; then
			pecho debug "	=> Skipping empty line"
		else
			pecho crit "	=> Rejecting line for syntax error: Only comments starting with\"#\" and simple KEY=VAL assignment is allowed"
			exit 200
		fi
		lineCount+=1
		unset lineContent
	done
	pecho info "Done verifying configuration"
}

function main() {
	pecho debug "Starting main function"
	if [[ ! "${configPath}" ]]; then
		pecho crit "Configuration not found"
		exit 1
	fi
	if [[ ! "${packerMode}" ]]; then
		pecho crit "Mode of operation not defined"
		exit 1
	fi
	if [[ ! "${desktopPath}" ]]; then
		pecho crit "Desktop file not found"
		exit 1
	fi
	if [[ ! "${_distribution}" ]]; then
		pecho crit "Distribution not set"
		exit 1
	fi
	configCheck "${configPath}"
	source "${configPath}"

	if [[ "${_distribution}" = archlinux && "${packerMode}" = copy ]]; then
		pecho info "Entering copy mode: Arch Linux"
		copyArchFiles
		exit 0
	fi
}

function copyArchFiles() {
	if [[ ! ${srcdir} || ! ${pkgdir} || ! ${pkgname} ]]; then
		pecho crit "srcdir and pkgdir not present! Did you forgot to export them before invoking packer?"
		exit 110
	fi
	if pacman -Qi "${archPackage}" 1>/dev/null 2>/dev/null; then
		pecho debug "Package present and verified"
	else
		pecho crit "Package does not exist"
		exit 201
	fi
	pacman -Ql "${archPackage}" >file.list

	while IFS= read -r line; do
		file="$(echo "$line" | awk '{print $2}')"
		if [[ -d ${file} ]]; then
			pecho debug "Omitting Directory"
		else
			if [[ -L "${file}" ]]; then
				mkdir -p "$(dirname "${pkgdir}/${file}")"
				ln -${installVerbose}sf "$(readlink -f "${file}")" "${pkgdir}/${file}"
			else
				install -${installVerbose}Dm755 "${file}" "${pkgdir}/${file}"
			fi
		fi
	done < file.list
	preCleanArch
	install "-${installVerbose}Dm644" "${configPath}" \
		"${pkgdir}/usr/lib/portable/info/${appID}/config"
	echo '#!/usr/bin/bash' >exec.sh
	echo "export _portableConfig=${appID}" >>exec.sh
	echo 'exec portable -- $@' >>exec.sh
	install \
		"-${installVerbose}Dm755" \
		exec.sh \
		"${pkgdir}/usr/bin/${pkgname}"
}

function preCleanArch() {
	rm -f "${pkgdir}/usr/share/applications"/*
	rm -f "${pkgdir}/usr/bin"/*
}

cmdlineDispatcher $@

main