#
# functions
#
# Copyright 2018, F123 Consulting, <information@f123.org>
# Copyright 2017, Mike Ray, <mike.ray@btinternet.com>
# Copyright 2018, Kyle, <kyle@free2.ml>
# Copyright 2018, StormDragon, <storm_dragon@linux-a11y.org>
#
# This is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free
# Software Foundation; either version 3, or (at your option) any later
# version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this package; see the file COPYING.  If not, write to the Free
# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
#--code--

usage() {
	scriptname=$0
	echo $(gettext "Usage:")
	echo "sudo $scriptname "

} >&2 # usage

out() {
	printf "$1 $2\n" "${@:3}"
} # out

error() {
	out "ERROR:" "$@"
} >&2 # error

die() {
	error "$@"
	usage
	exit 1
} # die

check_deps () {
	[ -f /usr/bin/parted ] || install_dep parted
	[ -f /usr/bin/pacstrap ] || install_dep arch-install-scripts
	[ -f /usr/bin/partx ] || pacman install_dep util-linux
	[ -f /usr/bin/dbus-launch ] || install_dep dbus
} # check_deps

install_dep () {
	local package=$1
	echo $(eval_gettext "Installing ${package}...")
	pacman -S $package --noconfirm --noprogressbar >/dev/null
} # install_dep

add_groups() {
	local chroot_=$1

	# input array holds commands to be ran inside chroot.
	input=(
		"groupadd --system input;"
		"groupadd --system uinput;"
		"groupadd --system pulse;"
		"groupadd --system pulse-access;"
		"useradd --system -g pulse -G audio -d /var/run/pulse -m -s /bin/nologin pulse;"
		"gpasswd -a pulse lp;"
		"gpasswd -a root pulse-access;"
		"echo 'KERNEL==\"event*\", NAME=\"input/%k\", MODE=\"660\", GROUP=\"input\"' >> /etc/udev/rules.d/99-input.rules;"
		"echo 'KERNEL==\"uinput\", SUBSYSTEM==\"misc\", OPTIONS+=\"static_node=uinput\", TAG+=\"uaccess\", GROUP=\"uinput\"' >> /etc/udev/rules.d/99-fenrirscreenreader.rules;"
		"mkdir -p /var/log/fenrirscreenreader/;"
		"chmod 755 /var/log/fenrirscreenreader/;"
		"touch /var/log/fenrirscreenreader/fenrir.log;"
		"chmod 662 /var/log/fenrirscreenreader/fenrir.log;"
	)
	chroot "${chroot_}" /bin/bash -c "${input[*]}" |& log
} # Add pulseaudio users and groups.

add_user() {
	local chroot_=$1
	local username=$2

	echo $(eval_gettext "Add user \${username}")
	input="useradd -m -g users -G wheel,storage,power,uucp,audio,games,pulse-access,lp,scanner,input,uinput -s /bin/bash ${username}"
	chroot "${chroot_}" /bin/bash -c "${input}" |& log
} # add_user

set_password() {
	local chroot_=$1
	local username=$2
	local userpwd=$3

	echo $(eval_gettext "Setting password for user \${username}")
	input="echo -e \"${userpwd}\n${userpwd}\n\" | passwd ${username}"
	chroot "${chroot_}" /bin/bash -c "${input}"
} # set_password

setup_locales() {
	local chroot=$1
    shift
	local defaultlocale=$1
    shift
    local locales=$@

	for l in $locales; do
        chroot $chroot /bin/sed -e "s/#${l}/${l}/" -i /etc/locale.gen
    done
	chroot $chroot /bin/bash -c locale-gen
	echo LANG=$defaultlocale > ${chroot}/etc/locale.conf
	echo LC_COLLATE=C >> ${chroot}/etc/locale.conf

} # setup_locales

setup_wifi() {
	local root=$1
	local iface=$2
	local ap=$3
	local pass=$4

	# Create the systemd network file
	mkdir -p $root/etc/systemd/network
	cat > $root/etc/systemd/network/$iface.network <<-endOfFile
		[Match]
		Name=$iface

		[Network]
		DHCP=yes
	endOfFile

	# Configure wpa_supplicant
	mkdir -p $root/etc/wpa_supplicant
	cat > $root/etc/wpa_supplicant/wpa_supplicant-$iface.conf <<-endOfFile
		ctrl_interface=/var/run/wpa_supplicant
		eapol_version=1
		ap_scan=1
		fast_reauth=1

		network={
		    ssid="$ap"
		    psk="$pass"
		    priority=1
		}
	endOfFile

	# Enable networking and wifi related services
	manage_service $root enable systemd-networkd
	manage_service $root enable wpa_supplicant@$iface
	manage_service $root enable systemd-resolved

	# Remove any legacy version of resolv.conf and link the version from systemd-resolved
	rm -f $root/etc/resolv.conf
	chroot $root /bin/bash -c "ln -sf /var/run/systemd/resolve/resolv.conf /etc/resolv.conf"
} #setup_wifi

download_package_databases () {
	local chroot=$1

	# Be sure the full package database for all repositories has been downloaded
	systemd-nspawn -a -q -D $1 pacman -Syy
} # download_package_databases

set_fake_hwclock () {
	local chroot=$1

	# Set an initial system time.
	systemd-nspawn -a -q -D $1 /usr/lib/systemd/scripts/fake-hwclock.sh save

	# Enable time sync
	systemd-nspawn -a -q -D $1 timedatectl set-ntp true
}

manage_service() {
	local chroot_=$1
	local operation=$2
	local service=$3

	echo $(eval_gettext "Calling systemctl to \${operation} \${service} service")
	systemd-nspawn -a -q -D "${chroot_}" systemctl "${operation}" "${service}"
} # manage_service

check_root() {
	(( EUID == 0 )) || return 1
	return 0
} # check_root

clean_up() {
	local WORKDIR=${1}

	if [ -f "${WORKDIR}/rpi-image-tool-mount-points" ]; then
		unmount_partitions ${WORKDIR}
		remove_mount_points ${WORKDIR}
		if cat ${WORKDIR}/rpi-image-tool-mount-points | grep loop >/dev/null; then
			remove_loop_devices ${WORKDIR}
		fi
	fi
	rm ${WORKDIR}/rpi-image-tool-mount-points
} # clean_up

delete_partitions() {
	local DEVICE=${1}

	for PART in `parted -ms /dev/sda -- print | perl -ne "if (m/^\d/) {print;}" | cut -f1 -d':' | sort -r`
	do
		parted ${DEVICE} -- rm ${PART} >/dev/null
	done
} # delete_partitions

loop_devices() {
	local WORKDIR=${1}
	local IMAGEFILE=${2}
	local __BOOTP=${3}
	local __ROOTP=${4}

	echo $(gettext "Setting up the loop device") ; echo
	LOOPDEVICE=$(losetup -P -f --show ${IMAGEFILE})
	BOOTP=${LOOPDEVICE}p1
	ROOTP=${LOOPDEVICE}p2
	eval $__BOOTP="'$BOOTP'"
	eval $__ROOTP="'$ROOTP'"
} # loop_devices

mount_device() {
	local WORKDIR=${1}
	local DEVICE=${2}
	local ROOTMP=${3}

	[ -b "${DEVICE}" ] || { echo "Supplied device name ${DEVICE} does not exist or is not a block device"; exit 1; }
	echo $(gettext "Making the work directory if it doesn't exist") ; echo
	[ -d "${WORKDIR}" ] || mkdir -p "${WORKDIR}"
	BOOTP=${DEVICE}1
	ROOTP=${DEVICE}2
	mount_partitions ${WORKDIR} ${BOOTP} ${ROOTP} ${BOOTMP} ${ROOTMP}
} # mount_device

mount_image() {
	local WORKDIR=${1}
	local IMAGEFILE=${2}
	local BOOTMP=${3}
	local ROOTMP=${4}

	[ -f "${IMAGEFILE}" ] || { echo "Supplied image file does not exist"; exit 1; }
	echo $(gettext "Making the work directory if it doesn't exist") ; echo
	[ -d "${WORKDIR}" ] || mkdir -p "${WORKDIR}"
	loop_devices ${WORKDIR} ${IMAGEFILE} BOOTP ROOTP
	mount_partitions ${WORKDIR} ${BOOTP} ${ROOTP} ${BOOTMP} ${ROOTMP}
} # mount_image

mount_partitions() {
	local WORKDIR=${1}
	local BOOTP=${2}
	local ROOTP=${3}
	local BOOTMP=${4}
	local ROOTMP=${5}

	echo $(gettext "Making the root mount point if it doesn't exist") ; echo
	[ -d "${WORKDIR}/${ROOTMP}" ] || mkdir -p "${WORKDIR}/${ROOTMP}"
	mount ${ROOTP} ${WORKDIR}/${ROOTMP}

	echo $(gettext "Making the boot mount point if it doesn't exist") ; echo
	[ -d "${WORKDIR}/${ROOTMP}/${BOOTMP}" ] || mkdir -p "${WORKDIR}/${ROOTMP}/${BOOTMP}"
	mount ${BOOTP} ${WORKDIR}/${ROOTMP}/${BOOTMP}

	echo $(eval_gettext "Writing mount-points to \$WORKDIR/rpi-image-tool-mount-points") ; echo
	REALROOTMOUNTPOINT=$(realpath ${WORKDIR}/${ROOTMP})
	REALBOOTMOUNTPOINT=$(realpath ${WORKDIR}/${ROOTMP}/${BOOTMP})
	echo -e "1:${BOOTP}:${REALBOOTMOUNTPOINT}\n2:${ROOTP}:${REALROOTMOUNTPOINT}" > ${WORKDIR}/rpi-image-tool-mount-points
} # mount_partitions

new_image() {
	local WORKDIR=${1}
	local IMAGEFILE=${2}
	local BOOTLABEL=${3}
	local ROOTLABEL=${4}
	local BOOTMP=${5}
	local ROOTMP=${6}
	local BLOCKCOUNT=${7}
	local BLOCKSIZE=${8}
	local SPLIT=${9}

	echo $(gettext "Making the work directory if it doesn't exist") ; echo
	[ -d ${WORKDIR} ] || mkdir -p "${WORKDIR}"

	echo $(gettext "Running dd to make a raw (zero-filled) image") ; echo
	dd if=/dev/zero of=${IMAGEFILE} bs=${BLOCKSIZE} count=${BLOCKCOUNT}

	echo $(gettext "Partitioning the raw image file") ; echo
	parted "${IMAGEFILE}" --script -- mklabel msdos
	echo $(gettext "Making the boot partition") ; echo
	parted "${IMAGEFILE}" --script -- mkpart primary fat32 1 ${SPLIT}
	echo $(gettext "Setting the boot partition bootable") ; echo
	parted "${IMAGEFILE}" --script set 1 boot on
	echo $(gettext "Making the root partition") ; echo
	parted "${IMAGEFILE}" --script -- mkpart primary ext4 ${SPLIT} -1

	local BOOTP
	local ROOTP

	loop_devices ${WORKDIR} ${IMAGEFILE} BOOTP ROOTP
	echo $(eval_gettext "Boot partition is \$BOOTP") ; echo
	echo $(eval_gettext "Root partition is \$ROOTP}") ; echo

	echo $(gettext "Making file systems") ; echo
	mkfs.vfat -n ${BOOTLABEL} ${BOOTP}
	mkfs.ext4 -L ${ROOTLABEL} ${ROOTP}

	mount_partitions ${WORKDIR} ${BOOTP} ${ROOTP} ${BOOTMP} ${ROOTMP}
} # new_image

prepare_device() {
	local WORKDIR=${1}
	local DEVICE=${2}
	local BOOTMP=${3}
	local ROOTMP=${4}
	local BLOCKCOUNT=${5}
	local BLOCKSIZE=${6}
	local SPLIT=${7}

	echo $(gettext "Making the work directory if it doesn't exist") ; echo
	[ -d ${WORKDIR} ] || mkdir -p "${WORKDIR}"

	echo $("Partitioning the device") ; echo
	parted "${DEVICE}" --script -- mklabel msdos
	echo $(gettext "Making the boot partition...") ; echo
	parted "${DEVICE}" --script -- mkpart primary fat32 1 ${SPLIT}
	echo $(gettext "Setting the boot partition bootable") ; echo
	parted "${DEVICE}" --script set 1 boot on
	echo $(gettext "Making the root partition") ; echo
	parted "${DEVICE}" --script -- mkpart primary ext4 ${SPLIT} -1
	echo $(eval_gettext "Boot partition is \${DEVICE}1") ; echo
	echo $(eval_gettext "Root partition is \${DEVICE}2") ; echo

	echo $(gettext "Making file systems") ; echo
	mkfs.vfat ${DEVICE}1
	mkfs.ext4 ${DEVICE}2

	mount_partitions ${WORKDIR} ${DEVICE}1 ${DEVICE}2 ${BOOTMP} ${ROOTMP}
} # prepare_device

remove_loop_devices() {
	local WORKDIR=${1}

	echo $(gettext "Removing loop devices") ; echo
	FIRSTPART=$(head -1 ${WORKDIR}/rpi-image-tool-mount-points | cut -f2 -d:)
	LOOP=${FIRSTPART%%p1}
	echo $(eval_gettext "Removing loop device \${LOOP}") ; echo
	losetup -d "${LOOP}"
} # remove_loop_devices

remove_mount_points() {
	local WORKDIR=${1}

	[ -f ${WORKDIR}/rpi-image-tool-mount-points ] || { echo "${WORKDIR}/rpi-image-tool-mount-points does not exist" ; exit 1 ; }
	cat ${WORKDIR}/rpi-image-tool-mount-points | cut -f3 -d: | \
	while read MOUNTPOINT
	do
		echo $(eval_gettext "Removing \$MOUNTPOINT") ; echo
		[ -d "${MOUNTPOINT}" ] && rmdir "${MOUNTPOINT}"
	done
} # remove_mount_points

unmount_partitions() {
	local WORKDIR=${1}

	if [ ! -f "${WORKDIR}/rpi-image-tool-mount-points" ]; then
		echo $(gettext "Could not find the file rpi-image-tool-mount-points in the work directory") ; echo
		exit 1
	fi

	echo $(gettext "umount partitions") ; echo
	cat ${WORKDIR}/rpi-image-tool-mount-points | cut -f3 -d: | \
	while read MOUNTPOINT
	do
		echo $(eval_gettext "umount \$MOUNTPOINT") ; echo
		umount "${MOUNTPOINT}"
	done
} # unmount partitions

validate_blockcount() {
	echo "$BLOCKCOUNT" | grep "^[0-9][0-9]*$" >/dev/null
	if [ $? -eq 1 ]; then
		echo $(gettext "Bad block count") ; echo
		usage
	fi
} # validate_blockcount

validate_blocksize() {
	declare -r BLOCKSIZE=${1}
	case "$BLOCKSIZE" in
		'1M'|'1MB')
		;;
		*)
			echo $(gettext "Invalid block-size") ; echo
			usage
			exit 1
		;;
	esac
} # validate_blocksize

validate_mountpoint() {
	declare -r MOUNTPOINT=${1}
	echo $MOUNTPOINT | grep "^[\./A-Za-z0-9\-][\.A-Za-z0-9\-]*" >/dev/null
	if [ $? -eq 1 ]; then
		echo $(gettext "Bad mount-point") ; echo
		exit 1
	fi
} # validate_mountpoint

validate_split() {
	local SPLIT=${1}
	echo "${SPLIT}" | grep "^[0-9][0-9]*$" >/dev/null
	if [ $? -eq 1 ]; then
		echo $(gettext "Bad split") ; echo
		usage
	fi
} # validate_split
