#!/bin/bash
# build.sh
# Automate Raspberry Pi image builds
# Take a config file on the command line and pass its options to the various stages of the build process
#
# Copyright 2018, F123 Consulting, <information@f123.org>
# Copyright 2018, Kyle, <kyle@free2.ml>
# Copyright 2018, Storm Dragon, <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--

export TEXTDOMAIN=build.sh
export TEXTDOMAINDIR=./locale

. gettext.sh

# Log writing function
log() {
    # Usage: command | log for just stdout.
    # Or command |& log for stderr and stdout.
    while read -r line ; do
        echo "$line" | sudo tee -a "$logFile"
    done
}
                                                                                                                                                                
# Log file name is /var/log/build.stormux
logFile="/var/log/build.stormux"

set -e
# Load the common functions
. scripts/functions

# Die if not running as root
if ! check_root ; then
	PROGNAME=$0
	echo $(eval_gettext "Script must be run as root, try: sudo \$PROGNAME")
	echo
	exit 1
fi

# Clear previous logs
echo -n | sudo tee "$logFile" &> /dev/null

declare -A argList=(
	[d:]="work-directory:,"
	[H:]="hostname:,"
	[h]="help,"
	[n:]="network-name:,"
	[o:]="output-file:,"
	[p:]="password:,"
	[r:]="root-password:,"
	[s:]="size:,"
	[u:]="user:,"
	[w:]="wifi-password:"
)

short="${!argList[*]}"
short="${short// /}"
long="${argList[*]}"
long="${long// /}"


# Get command line options
if ! options=$(getopt -o $short -l $long -n "build.sh" -- "$@"); then
	echo "Unknown options passed to $0" | log
	exit 1
fi

eval set -- "$options"

# Declare the overrides that will replace configuration file variables with  command line options
declare -A overrides=()

while [[ $# -gt 0 ]]; do
	case $1 in
			-d|--work-directory) overrides[workdir]="$2"; shift 2 ;;
		-H|--hostname) overrides[hostname]="$2"; shift 2 ;;
		-h|--help) cat <<-HELP
			$(gettext "Usage")
			$(eval_gettext "\$name [options...]")
			$(gettext "Options")
			-d, --work-directory: $(gettext "The work directory for caching packages and mounting filesystems")
			-H, --hostname: $(gettext "The hostname of the installed system")
			-h, --help: $(gettext "Print this help and exit")
			-n, --network-name: $(gettext "The name of the wifi network. If specified, a wifi password is also required.")
			-o, --output-file: $(gettext "The name of the image file to write")
			-p, --password: $(gettext "The password of the regular non-root user")
			-r, --root-password: $(gettext "The password of the root user")
			-s --size: $(gettext "The initial size of the image file in MB")
			-u, --user: $(gettext "The name of the regular non-root user")
			-w, --wifi-password: $(gettext "the password used to login to the specified wifi network, requires network name")
		HELP
		exit 0 ;;
		-n|--network-name) overrides[networkname]="$2"; shift 2 ;;
		-o|--output-file) overrides[imagename]="$2"; shift 2 ;;
		-p|--password) overrides[userpass]="$2"; shift 2 ;;
		-r|--root-password) overrides[rootpass]="$2"; shift 2 ;;
		-s|--size) overrides[imagesize]="$2"; shift 2 ;;
		-u|--user) overrides[username]="$2"; shift 2 ;;
		-w|--wifi-password) overrides[wifipass]="$2"; shift 2 ;;
		(--) shift ;;
		(*)
			if [[ -f "$1" ]]; then
				source "$1"
			else
				echo "Invalid configuration file specified." | log
				exit 1
			fi
			shift
		;;
	esac
done

# Replace configuration variables using the overrides gathered from command line options
if [[ "${overrides[workdir]}" ]]; then
    workdir="${overrides[workdir]}"
fi
if [[ "${overrides[hostname]}" ]]; then
    hostname="${overrides[hostname]}"
fi
if [[ "${overrides[networkname]}" ]]; then
    networkname="${overrides[networkname]}"
fi
if [[ "${overrides[imagename]}" ]]; then
    imagename="${PWD}/${overrides[imagename]}"
fi
if [[ "${overrides[userpass]}" ]]; then
    userpass="${overrides[userpass]}"
fi
if [[ "${overrides[rootpass]}" ]]; then
    rootpass="${overrides[rootpass]}"
fi
if [[ "${overrides[imagesize]}" ]]; then
    imagesize="${overrides[imagesize]}"
fi
if [[ "${overrides[username]}" ]]; then
username="${overrides[username]}"
fi
if [[ "${overrides[wifipass]}" ]]; then
    wifipass="${overrides[wifipass]}"
fi

# export workdir so it is available in subshells.
export workdir

# All build stage scripts run from the scripts directory underneath the directory where this build script resides.
# todo: Call the loaded functions as needed rather than calling the stage scripts.
# First, the current directory must be saved.
current=$PWD

# Now we enter the directory where the stage scripts live.
cd scripts && echo "cd $PWD" | log

# Check for dependencies and install any that are missing.
check_deps

# Before building an image, we must be sure that the work directory is clean except for a package cache.
# There is not yet a facility to complete a half-built failed image, but we can cache software packages.
if [ -d "$workdir" ]; then
    clean_up "$workdir" || true
    # Don't remove an otherwise empty work directory if a pacman cache is present
    [[ -d "${workdir}/pacman-cache" ]] || rm -Rv "$workdir" |& log
fi

# Start by creating an empty image and mounting it.
new_image $workdir $imagename $bootlabel $rootlabel boot root $imagesize 1M 64M |& log

# Install packages available from the official ALARM repositories.
./pacstrap -l "$packagelist"  -c "${workdir}/pacman-cache" "$workdir/root" |& log

# Set the system hostname
echo $hostname > $workdir/root/etc/hostname

# The root partition is automatically mounted by systemd. Add only the boot partition information to fstab.
echo -e "\n/dev/mmcblk0p1  /boot   vfat    defaults        0       0" >> $workdir/root/etc/fstab

# Set the root password
set_password "${workdir}/root" root "${rootpass}"

# Install locale specific packages. The file name looks like the configured package list file, but includes an @ sign followed by the locale followed by .list, e.g. packages@sw_KE.list.
# This file will be ignored if it doesn't exist.
test -f "${packagelist%%.list}@${locale}.list" && ./pacstrap -l "${packagelist%%.list}@${locale}.list" -c "${workdir}/pacman-cache" "$workdir/root"

# Add pulseaudio stuff and other groups
add_groups "${workdir}/root" |& log

# Add the regular user and set its password
add_user "${workdir}/root" "${username}" |& log
set_password "${workdir}/root" "${username}" "${userpass}" |& log

# Copy any override files into the new system. This overrides any system and user configuration files or scripts installed with packages.
# Remove any stale temporary files related to the file and content repositories
rm -Rfv /tmp/stormux |& log

# Start by cloning the stormux repository to /tmp
git clone -b $(git branch | grep \* | cut -f2 -d \ ) --recurse-submodules https://gitlab.com/stormux/stormux.git /tmp/stormux |& log

 # Copy in the files.
cp -Rv /tmp/stormux/files/files/* "${workdir}/root/" |& log
# Generate translations:
find /tmp/stormux/files/locales -type f -iname '*.po' -exec bash -c '
    for i ; do
        # i is in file, o is outfile.
        language="$(grep "^\"Language: .*\\n" "${i}" | cut -d " " -f2 | cut -d "\\" -f1)"
        # Handle exceptions for languages.
        case "$language" in
            *) language="${language::2}";;
        esac
        o="${i##*/}"
        o="${o%.po}.mo"
        msgfmt "$i" -o "${workdir}/root/usr/share/locale/${language}/LC_MESSAGES/${o}"
    done' _ {} +
mkdir -p "${workdir}/root/usr/share/doc/stormux" |& log
cp -Rv /tmp/stormux/content/stormux/* "${workdir}/root/usr/share/doc/stormux/" |& log

# Set the system locale and generate all other locales specified in the configuration file.
# The default system locale must be set before we can copy user files to $HOME.
setup_locales $workdir/root $defaultlocale ${locales[@]} |& log

# Always copy the contents of /etc/skel to the home directory of the user that was created earlier
systemd-nspawn -a -q -D $workdir/root\
	sudo -u \#1000\
	bash -c "shopt -s dotglob && cp -R /etc/skel/* \"\$HOME\"" |& log

# Enable system services
for service in $services; do
	manage_service "${workdir}/root" enable "${service}" |& log
done

# Globally enable dbus-broker
systemd-nspawn -a -q -D $workdir/root\
	systemctl --global enable dbus-broker.service |& log

# Download package databases
download_package_databases $workdir/root |& log
set_fake_hwclock $workdir/root |& log

# Configure wifi if network information is present in the configuration file.
if [[ -n "$networkname" ]]; then
    setup_wifi $workdir/root wlan0 $networkname $wifipass |& log
fi

# Touch the firstboot file. It will be deleted by the firstboot script that looks for it.
systemd-nspawn -a -q -D $workdir/root\
	sudo -u \#1000\
	bash -c "touch \$HOME/.firstboot"

# Write a system timestamp.
# This timestamp will determine which incremental updates apply to this image
echo "$(date +%y%m%d%H%M)" > $workdir/root/etc/timestamp-stormux

# Unmount the image file
clean_up "$workdir" |& log
# Keep the work directory if a pacman cache exists, otherwise remove it
if [[ ! -d "${workdir}/pacman-cache" ]]; then
    rm -Rv "$workdir" |& log
fi

# Once all scripts have completed, come back to the directory from which this script was launched.
cd $current && echo "cd $current" |& log

newrootsize=$(ls -hs "${imagename}" | cut -f1 -d' ')
relativeimage=$(realpath --relative-to="$PWD" "$imagename")
echo "$relativeimage was built successfully" | log
echo "Size of $relativeimage is ${newrootsize}" | log

exit 0
