#!/bin/bash
# spell-checker:ignore termux keyevent sdcard binutils unmatch adb's dumpsys logcat pkill

# There are three shells: the host's, adb, and termux. Only adb lets us run
# commands directly on the emulated device, only termux provides a GNU
# environment on the emulated device (to e.g. run cargo). So we use adb to
# launch termux, then to send keystrokes to it while it's running.
# This means that the commands sent to termux are first parsed as arguments in
# this shell, then as arguments in the adb shell, before finally being used as
# text inputs to the app. Hence, the "'wrapping'" on those commands.
# There's no way to get any feedback from termux, so every time we run a
# command on it, we make sure it ends by creating a unique *.probe file at the
# end of the command. The contents of the file are used as a return code: 0 on
# success, some other number for errors (an empty file is basically the same as
# 0). Note that the return codes are text, not raw bytes.


this_repo="$(dirname $(dirname -- "$(readlink -- "${0}")"))"

help () {
    echo \
"Usage: $0 COMMAND [ARG]

where COMMAND is one of:
  snapshot APK  install APK and dependencies on an emulator to prep a snapshot
                (you can, but probably don't want to, run this for physical
                devices -- just set up termux and the dependencies yourself)
  sync [REPO]   push the repo at REPO to the device, deleting and restoring all
                symlinks (locally) in the process; by default, REPO is:
                $this_repo
  build         run \`cargo build --features feat_os_unix_android\` on the
                device, then pull the output as build.log
  tests         run \`cargo test --features feat_os_unix_android\` on the
                device, then pull the output as tests.log

If you have multiple devices, use the ANDROID_SERIAL environment variable to
specify which to connect to."
}

hit_enter() {
    adb shell input keyevent 66
}

launch_termux() {
    echo "launching termux"
    if ! adb shell 'am start -n com.termux/.HomeActivity' ; then
        echo "failed to launch termux"
        exit 1
    fi
    # the emulator can sometimes be a little slow to launch the app
    while ! adb shell 'ls /sdcard/launch.probe' 2>/dev/null; do
        echo "waiting for launch.probe"
        sleep 5
        adb shell input text 'touch\ /sdcard/launch.probe' && hit_enter
    done
    echo "found launch.probe"
    adb shell 'rm /sdcard/launch.probe' && echo "removed launch.probe"
}

run_termux_command() {
    command="$1"  # text of the escaped command, including creating the probe!
    probe="$2"  # unique file that indicates the command is complete
    launch_termux
    adb shell input text "$command" && hit_enter
    while ! adb shell "ls $probe" 2>/dev/null; do echo "waiting for $probe"; sleep 30; done
    return_code=$(adb shell "cat $probe")
    adb shell "rm $probe"
    echo "return code: $return_code"
    return $return_code
}

snapshot () {
    apk="$1"
    echo "running snapshot"
    adb install -g "$apk"
    probe='/sdcard/pkg.probe'
    command="'yes | pkg install rust binutils openssl -y; touch $probe'"
    run_termux_command "$command" "$probe"
    echo "snapshot complete"
    adb shell input text "exit" && hit_enter && hit_enter
}

sync () {
    repo="$1"
    echo "running sync $1"
    # android doesn't allow symlinks on shared dirs, and adb can't selectively push files
    symlinks=$(find "$repo" -type l)
    # dash doesn't support process substitution :(
    echo $symlinks | sort >symlinks
    git -C "$repo" diff --name-status | cut -f 2 >modified
    modified_links=$(join symlinks modified)
    if [ ! -z "$modified_links" ]; then
        echo "You have modified symlinks. Either stash or commit them, then try again: $modified_links"
        exit 1
    fi
    if ! git ls-files --error-unmatch $symlinks >/dev/null; then
        echo "You have untracked symlinks. Either remove or commit them, then try again."
        exit 1
    fi
    rm $symlinks
    # adb's shell user only has access to shared dirs...
    adb push "$repo" /sdcard/coreutils
    git -C "$repo" checkout $symlinks
    # ...but shared dirs can't build, so move it home as termux
    probe='/sdcard/mv.probe'
    command="'cp -r /sdcard/coreutils ~/; touch $probe'"
    run_termux_command "$command" "$probe"
}

build () {
    probe='/sdcard/build.probe'
    command="'cd ~/coreutils && cargo build --features feat_os_unix_android 2>/sdcard/build.log; echo \$? >$probe'"
    echo "running build"
    run_termux_command "$command" "$probe"
    return_code=$?
    adb pull /sdcard/build.log .
    cat build.log
    return $return_code
}

tests () {
    probe='/sdcard/tests.probe'
    export RUST_BACKTRACE=1
    command="'cd ~/coreutils && timeout --preserve-status --verbose -k 1m 60m cargo test --features feat_os_unix_android --no-fail-fast >/sdcard/tests.log 2>&1; echo \$? >$probe'"
    run_termux_command "$command" "$probe"
    return_code=$?
    adb pull /sdcard/tests.log .
    cat tests.log
    return $return_code
}

#adb logcat &
exit_code=0

if [ $# -eq 1 ]; then
    case "$1" in
        sync)     sync "$this_repo"; exit_code=$?;;
        build)    build; exit_code=$?;;
        tests)    tests; exit_code=$?;;
        *)        help;;
    esac
elif [ $# -eq 2 ]; then
    case "$1" in
        snapshot) snapshot "$2"; exit_code=$?;;
        sync)     sync "$2"; exit_code=$?;;
        *)        help; exit 1;;
    esac
else
    help
    exit_code=1
fi

#pkill adb
exit $exit_code
