#!/usr/bin/env bash
set -Eeuo pipefail
export TEXTDOMAIN=${TEXTDOMAIN:-eczos-recovery-media}
export TEXTDOMAINDIR=${TEXTDOMAINDIR:-/usr/share/locale}
_() { gettext "$1"; }

die() { printf '%s: %s\n' "$(_ 'ERROR')" "$*" >&2; exit 1; }
[[ $(id -u) -eq 0 ]] || die "$(_ 'the writer must run as root through system authentication')"
[[ $# -eq 3 || ($# -eq 4 && ($4 == --validate-only || $4 == --json-progress)) ]] || die "$(_ 'invalid invocation')"
mode=$1
image=$(realpath -e -- "$2")
target=$(readlink -f -- "$3")
validate_only=false
json_progress=false
[[ ${4:-} == --validate-only ]] && validate_only=true
[[ ${4:-} == --json-progress ]] && json_progress=true
[[ -f $image && -r $image ]] || die "$(_ 'the installation image is not readable')"
[[ $target == /dev/* && -b $target ]] || die "$(_ 'the target is not a block device')"

top_disk() {
    local node parent
    node=$(readlink -f -- "$1")
    while parent=$(lsblk -dnro PKNAME -- "$node" 2>/dev/null) && [[ -n $parent ]]; do
        node=/dev/$parent
    done
    printf '%s\n' "$node"
}

root_source=$(findmnt -nro SOURCE /)
root_disk=$(top_disk "$root_source")
target_disk=$(top_disk "$target")
[[ $target_disk != "$root_disk" ]] || die "$(_ 'the system drive is never overwritten')"

source_mount=$(findmnt -nro SOURCE --target "$image" 2>/dev/null || true)
if [[ -n $source_mount && $source_mount == /dev/* ]]; then
    [[ $(top_disk "$source_mount") != "$target_disk" ]] || \
        die "$(_ 'the installation image is stored on the selected target medium')"
fi

case $mode in
    disk)
        [[ $(lsblk -dnro TYPE -- "$target") == disk ]] || die "$(_ 'select the complete USB drive or SD card')"
        removable=$(lsblk -dnro RM -- "$target")
        transport=$(lsblk -dnro TRAN -- "$target" 2>/dev/null || true)
        [[ $removable == 1 || $transport == usb || $transport == mmc || $target == /dev/mmcblk* ]] || \
            die "$(_ 'only a removable USB drive or SD card is allowed')"
        image_size=$(stat -c %s -- "$image")
        target_size=$(blockdev --getsize64 "$target")
        (( target_size >= image_size )) || die "$(_ 'the target medium is smaller than the installation image')"
        if $validate_only; then
            printf "$(_ 'Validation passed: %s can be written to %s.')\n" "$image" "$target"
            exit 0
        fi
        while IFS= read -r child; do
            if findmnt -rn -S "$child" >/dev/null 2>&1; then
                umount -- "$child" || die "$(printf "$(_ 'cannot unmount %s')" "$child")"
            fi
        done < <(lsblk -lnpo NAME -- "$target")
        printf "$(_ 'Writing to %s. This may take some time...')\n" "$target"
        if $json_progress; then
            printf 'ECZOS_EVENT {"type":"progress","fraction":0.01,"message":"%s"}\n' "$(_ 'Preparing recovery media…')"
            LC_ALL=C dd if="$image" of="$target" bs=4M iflag=fullblock conv=fsync status=progress \
                2> >(tr '\r' '\n' | awk -v total="$image_size" -v writing="$(_ 'Writing recovery media…')" -v about="$(_ 'about')" -v remaining_text="$(_ 'min remaining')" '
                    BEGIN { started = systime() }
                    /^[0-9]+ bytes/ {
                        bytes = $1
                        fraction = bytes / total
                        if (fraction > 1) fraction = 1
                        elapsed = systime() - started
                        remaining = (bytes > 0 && elapsed > 0) ? int(((total - bytes) * elapsed / bytes) / 60 + 0.5) : 0
                        percent = int(fraction * 100)
                        message = writing " " percent "%"
                        if (remaining > 0) message = message " • " about " " remaining " " remaining_text
                        printf "ECZOS_EVENT {\"type\":\"progress\",\"fraction\":%.4f,\"message\":\"%s\"}\n", fraction, message
                        fflush()
                    }')
        else
            dd if="$image" of="$target" bs=4M iflag=fullblock conv=fsync status=progress
        fi
        sync
        blockdev --rereadpt "$target" 2>/dev/null || true
        ;;
    dvd)
        [[ $(lsblk -dnro TYPE -- "$target") == rom ]] || die "$(_ 'the target is not an optical drive')"
        if $validate_only; then
            printf "$(_ 'Validation passed: %s can be burned to %s.')\n" "$image" "$target"
            exit 0
        fi
        printf "$(_ 'Burning to %s. This may take some time...')\n" "$target"
        if $json_progress; then
            printf 'ECZOS_EVENT {"type":"progress","fraction":0.05,"message":"%s"}\n' "$(_ 'Burning disc… this may take some time')"
            LC_ALL=C xorriso -as cdrecord -v dev="$target" blank=as_needed -eject "$image" \
                2> >(tr '\r' '\n' | awk -v burning="$(_ 'Burning disc…')" -v about="$(_ 'about')" -v remaining_text="$(_ 'min remaining')" '
                    BEGIN { started = systime() }
                    {
                        print > "/dev/stderr"
                        for (field = 2; field < NF; field++) {
                            if ($field == "of" && $(field - 1) + 0 > 0 && $(field + 1) + 0 > 0) {
                                written = $(field - 1) + 0
                                total = $(field + 1) + 0
                                fraction = written / total
                                if (fraction > 1) fraction = 1
                                elapsed = systime() - started
                                remaining = (written > 0 && elapsed > 0) ? int(((total - written) * elapsed / written) / 60 + 0.5) : 0
                                percent = int(fraction * 100)
                                message = burning " " percent "%"
                                if (remaining > 0) message = message " • " about " " remaining " " remaining_text
                                printf "ECZOS_EVENT {\"type\":\"progress\",\"fraction\":%.4f,\"message\":\"%s\"}\n", fraction, message
                                fflush()
                                break
                            }
                        }
                    }')
        else
            xorriso -as cdrecord -v dev="$target" blank=as_needed -eject "$image"
        fi
        ;;
    *) die "$(_ 'unknown media type')" ;;
esac
printf '\n%s\n' "$(_ 'Done. The ECZOS recovery medium has been written and can be removed safely.')"
$json_progress && printf 'ECZOS_EVENT {"type":"progress","fraction":1.0,"message":"%s"}\n' "$(_ 'Done. The recovery medium can be removed safely.')"
