#!/bin/bash

# constants
VERSION='@VERSION@'     # updated at install time
IMAGE_SIZE_MARGIN_KB=0  # fs size estimation is enough pessimistic
MKSQUASHFS_OPTS="-b 1M -comp xz"
ONE_GIGABYTE_KB=$((1024*1024))
MAX_WORK_IMAGE_OVERHEAD_KB=$((2*ONE_GIGABYTE_KB))
DEBUG=0
DD="dd status=none"
DBSTCK_DIR="/usr/share/debootstick"

# get cmdline parsing and os-detection functions
. $DBSTCK_DIR/scripts/create-image/cmdline
. $DBSTCK_DIR/scripts/create-image/os-detect

# check options
root_password_request="NO_REQUEST"
root_password_on_first_boot=0
config_grub_on_serial_line=0
system_type="live"
kernel_bootargs=""
config_hostname=""
chroot_in=""
image_out=""

parse_args()
{
    while [ $# != 0 ]
    do
        case "$1" in
            -h|--help)
                usage_and_exit 0
            ;;
            --help-os-support)
                describe_os_support
                exit 0
            ;;
            -v|--version)
                echo "debootstick $VERSION"
                exit 0
            ;;
            --kernel-package)
                kernel_package="$2"
                shift 2
            ;;
            --config-kernel-bootargs)
                kernel_bootargs="$2"
                shift 2
            ;;
            --config-root-password-ask)
                root_password_request="ASK"
                shift
            ;;
            --config-root-password-none)
                root_password_request="NO_PASSWORD"
                shift
            ;;
            --config-root-password-first-boot)
                root_password_on_first_boot=1
                shift
            ;;
            --config-hostname)
                config_hostname="$2"
                shift 2
            ;;
            --config-grub-on-serial-line)
                config_grub_on_serial_line=1
                shift
            ;;
            --system-type)
                system_type="$2"
                shift 2
            ;;
            *)
                break
            ;;
        esac
    done

    # we need 2 more args
    if [ -z "$2" ]
    then
        usage_and_exit 1
    fi

    chroot_in="$1"
    image_out="$2"
}

parse_args "$@"

# let's verify system_type variable
case "$system_type" in
    'live'|'installer')
        ;;  # ok
    *)
        echo "--system-type option value must be either 'live' or 'installer'." >&2
        exit 1
esac

# ensure we are root
if [ $(id -u) -ne 0 ]; then
    echo "debootstick should be run as root. Trying sudo..."
    exec sudo "$0" "$@"
fi

# check that $image_out is a writable file path
if [ ! -w $(dirname "$image_out") ]
then
    usage_and_exit
fi

# $chroot_in should be a directory
if [ ! -d "$chroot_in" ]
then
    usage_and_exit
fi

# this directory should contain a system 
# file hierarchy (1st level of checks)
check_fs_hierarchy "$chroot_in" 1 || exit 1

# detect target type
target_type="$(detect_target_type "$chroot_in")"
[ -z "$target_type" ] && exit 1

# get common and target-specific functions
functions="$(
    cat "$DBSTCK_DIR"/scripts/create-image/common/*
    cat $(find "$DBSTCK_DIR"/scripts/create-image/target/$target_type/ \
                    -type f ! -name detect.sh)
)"

# have them available here and in chrooted scripts
eval "$functions"
export chrooted_functions="$functions"
probe_target_optional_functions

if $target_get_bootloader_install_command_exists
then
    bootloader_install_command=$(target_get_bootloader_install_command)
fi

if [ "$system_type" = "installer" ]
then
    if [ -z "$bootloader_install_command" ] || ! $(target_use_lvm)
    then
        # cannot use installer mode if target does not use LVM or does not
        # specify a bootloader installation procedure.
        echo "Sorry, this target does not support installer system type." >&2
        exit 1
    fi
fi

# if we are here, command line is ok :)
if [ "$root_password_request" = "ASK" ]
then
    while true
    do
        read -s -p "Enter embedded-os root password: " passwd1
        echo
        read -s -p "Enter embedded-os root password again: " passwd2
        echo
        if [ "$passwd1" = "$passwd2" ]
        then
            echo 'OK'
            root_password_request="root:$passwd1"
            break
        else
            echo 'Sorry, passwords do not match, please retry.'
        fi
    done
fi

ORIG_TREE="$(cd "$chroot_in"; pwd)"
STICK_OS_ID=$(uuidgen | tr -d '-' | head -c 8)
DBSTCK_TMPDIR=$(mktemp -du --tmpdir tmp.dbstck.XXXXX.d)
final_image_path="$image_out"
final_image_abspath="$(abspath "$image_out")"
if [ "$DEBUG" = "1" ]
then
    CHROOTED_DEBUG="--debug"
fi

final_cleanup()
{
    return_code=$1
    if [ "$1" -gt 0 ]   # if error
    then
        rm -f $final_image_abspath
    fi
}

echo "I: detected target system: $(target_get_name)"

start_failsafe_mode --toplevel final_cleanup

failsafe mkdir -p $DBSTCK_TMPDIR
cd $DBSTCK_TMPDIR

# execute target-specific preliminary steps, if any
optional_target_preliminary_steps ${STICK_OS_ID}

# step: compute a stick size large enough for our work
# (i.e. not for the final minimized version)
echo -n "I: draft image - computing a size large enough... "
fs_size_estimation_kb=$(estimated_size_kb $ORIG_TREE)
draft_stick_size_kb=$((fs_size_estimation_kb +
                       MAX_WORK_IMAGE_OVERHEAD_KB))
echo done

# step: create draft image structure
echo -n "I: draft image - partitioning and formatting... "
create_formatted_image \
        draft \
        $draft_stick_size_kb \
        $STICK_OS_ID
echo done

# step: copy original tree to work image and modify it
echo -n "I: draft image - copying filesystem tree... "
cd $draft_rootfs_mountpoint
cp -a $ORIG_TREE/* .
echo done

# execute target-specific preparation steps, if any
optional_target_prepare_rootfs draft outside

# 2nd level of checks of input file hierarchy
check_fs_hierarchy "$PWD" 2 || exit 1

mkdir -p opt/debootstick
cp -a $DBSTCK_DIR/scripts/live opt/debootstick/live
cp -a $DBSTCK_DIR/scripts/create-image/chrooted-customization-draft.sh .
# we will need internet connectivity for package
# management in the chroot. Ensure we have a valid DNS setup there.
[ -f etc/resolv.conf ] || touch etc/resolv.conf
with mount -o bind /etc/resolv.conf $PWD/etc/resolv.conf; do
    with mount -o bind /run $PWD/run; do
        # let's start the customization
        chroot . ./chrooted-customization-draft.sh $CHROOTED_DEBUG    \
                "$draft_device" "$root_password_request"        \
                stick_os_id=$STICK_OS_ID   \
                config_grub_on_serial_line=$config_grub_on_serial_line  \
                kernel_package="\"$kernel_package\""    \
                kernel_bootargs="\"$kernel_bootargs\"" \
                config_hostname="\"$config_hostname\""
    done
done
rm ./chrooted-customization-draft.sh

# execute target-specific cleanup steps, if any
optional_target_cleanup_rootfs draft outside

# step: finalyse filesystem setup
finalize_fs $draft_rootfs_mountpoint

# step: compute minimal size of final stick
echo -n "I: final image - computing minimal image size... "
cd $DBSTCK_TMPDIR
final_rootpart_size_kb=$(estimate_minimal_rootpart_size_kb \
                            $draft_rootfs_mountpoint)
draft_rootpart_size_kb=$(device_size_kb $draft_rootpart_device)
# since the draft and final images are formatted the
# same way (only the size of the root partition differs),
# (final_stick_size_kb - final_rootpart_size_kb)
# should be equal to
# (draft_stick_size_kb - draft_rootpart_size_kb)
# let's compute final_stick_size_kb accordingly:
final_stick_size_kb=$((
        final_rootpart_size_kb +
        draft_stick_size_kb -
        draft_rootpart_size_kb
))
echo done

# step: copy work version to the final image (with minimal size)

# prepare a final image with minimal size
echo -n "I: final image - partitioning and formatting... "
create_formatted_image \
        final \
        $final_stick_size_kb \
        ${STICK_OS_ID} \
        $final_image_abspath
echo done
echo -n "I: final image - copying content from draft image... "
cp -a $draft_rootfs_mountpoint/* $final_rootfs_mountpoint/
echo done
release_image draft     # not needed anymore

# complete the dbstck.conf file
cat >> $final_rootfs_mountpoint/dbstck.conf << EOF
STICK_OS_ID=$STICK_OS_ID
USE_LVM=$(target_use_lvm)
SYSTEM_TYPE=$system_type
ASK_ROOT_PASSWORD_ON_FIRST_BOOT=$root_password_on_first_boot
BOOTLOADER_INSTALL=$bootloader_install_command
EOF

# step: customize final OS
cd $final_rootfs_mountpoint

# execute target-specific preparation steps, if any
optional_target_prepare_rootfs final outside

# since the size of the filesystem mounted there is minimized,
# creating new files may cause problems.
# so we will use the directory /tmp that we mount in memory.
with mount -t tmpfs none $final_rootfs_mountpoint/tmp; do
    with mount -o bind /run $PWD/run; do
        cp -a $DBSTCK_DIR/scripts/create-image/chrooted-customization-final.sh tmp
        chroot . tmp/chrooted-customization-final.sh "$final_device"
    done
done

# execute target-specific cleanup steps, if any
optional_target_cleanup_rootfs final outside

cd ..

# execute target-specific final steps, if any
optional_target_final_customization_steps ${STICK_OS_ID}

# step: clean up
echo -n "I: cleaning up... "
undo_all
echo done

chmod a+rw $final_image_abspath
stick_size=$(real_size_human_readable $final_image_abspath)
echo "I: $final_image_path ready (size: ${stick_size}). "

