#!/bin/sh
# fgfs-run - single entry point for all FlightGear backends on Sailfish OS.
#
#   fgfs-run [--backend=zink|gles2|gles3] [--region=LAT,LON[,RADIUS]] [fgfs options...]
#
# Picks the library paths and environment for the chosen backend, makes sure
# the scenery for a region is there first (checked offline against the
# regions fgfs-scenery has finished; fetched only when missing, or always
# with FGFS_SCENERY_REFRESH=1), and execs the matching binary.
# Without --backend it uses FGFS_BACKEND, or zink, which is the only backend
# with the PUI menus, the HUD and the canvas displays.
#
# POSIX shell on purpose: /bin/bash on the device is not a full bash and has
# no arrays.
set -e

BACKEND=${FGFS_BACKEND:-zink}
REGION=""
RADIUS=1
FGROOT=${FGFS_ROOT:-$HOME/.local/share/harbour-fgview/fgdata}
SCENERY=${FGFS_SCENERY:-$HOME/.fgfs/TerraSync}

# Collect the options meant for fgfs in the positional parameters.
collected=""
for arg in "$@"; do
    case "$arg" in
        --backend=*) BACKEND="${arg#*=}"; continue ;;
        --region=*)  REGION="${arg#*=}"; continue ;;
        --fg-root=*) FGROOT="${arg#*=}" ;;
        --list-backends)
            echo "zink   /opt/fgfs                        Mesa/Zink, full feature set (menus, HUD, canvas)"
            echo "gles2  /home/.system/fgfs/fgfs-gles      native GLES2, no fixed-function features"
            echo "gles3  /home/.system/fgfs/fgfs-gles3     native GLES3, no fixed-function features"
            exit 0 ;;
    esac
    collected="$collected $arg"
done
# shellcheck disable=SC2086
set -- $collected

# --- pick up the scenery for a region before starting -----------------------
if [ -n "$REGION" ]; then
    LAT=${REGION%%,*}
    REST=${REGION#*,}
    LON=${REST%%,*}
    case "$REST" in *,*) RADIUS=${REST##*,} ;; esac
    if [ -x /opt/fgfs/bin/fgfs-scenery ]; then
        # Offline first: a finished fetch around the airport is on record,
        # so there is nothing to ask the mirrors.  FGFS_SCENERY_REFRESH=1
        # forces the comparison with the servers anyway.
        # exit 0: a finished fetch covers it; 2: tiles on the disk, no record
        # (fetched before there were records) - both good enough to start
        if [ "${FGFS_SCENERY_REFRESH:-0}" = 1 ]; then
            rc=1
        else
            /opt/fgfs/bin/fgfs-scenery --check --lat "$LAT" --lon "$LON" --radius "$RADIUS" \
                --target "$SCENERY" --subtrees Terrain,Objects 2>/dev/null
            rc=$?
        fi
        if [ "$rc" = 0 ] || [ "$rc" = 2 ]; then
            echo "fgfs-run: Szenerie fuer $LAT,$LON vorhanden"
        else
            echo "fgfs-run: Szenerie fuer $LAT,$LON (Radius $RADIUS Grad) wird geladen ..."
            if /opt/fgfs/bin/fgfs-scenery --lat "$LAT" --lon "$LON" --radius "$RADIUS" \
                   --target "$SCENERY" --subtrees Terrain,Objects; then
                echo "fgfs-run: Szenerie fuer $LAT,$LON geladen"
            else
                echo "fgfs-run: Szenerie-Abgleich fehlgeschlagen, starte mit dem Bestand auf der Platte"
            fi
        fi
    else
        echo "fgfs-run: fgfs-scenery fehlt, ueberspringe den Abgleich" >&2
    fi
fi
[ -d "$SCENERY/Terrain" ] && set -- "$@" "--fg-scenery=$SCENERY"

# --- backend specific environment -------------------------------------------
# The install prefixes are not consistent about lib vs lib64, and a prefix can
# have both, so take every directory that exists rather than guessing one.
libdirs() {
    out=""
    for prefix in "$@"; do
        for d in "$prefix/lib64" "$prefix/lib"; do
            [ -d "$d" ] || continue
            if [ -z "$out" ]; then out="$d"; else out="$out:$d"; fi
        done
    done
    echo "$out"
}

# The osgPlugins directory sits next to libosg, wherever that turned out to be.
plugindir() {
    for prefix in "$@"; do
        for d in "$prefix/lib64" "$prefix/lib"; do
            for p in "$d"/osgPlugins-*; do
                [ -d "$p" ] && { echo "$p"; return; }
            done
        done
    done
}

case "$BACKEND" in
    zink)
        PREFIX=/opt/fgfs
        LD_LIBRARY_PATH="$(libdirs /opt/mesa-zink /opt/fgfs)"
        __EGL_VENDOR_LIBRARY_DIRS=/opt/mesa-zink/share/glvnd/egl_vendor.d
        MESA_LOADER_DRIVER_OVERRIDE=zink
        # lazy descriptors are worth about 30 percent on the Mali
        ZINK_DESCRIPTORS=${ZINK_DESCRIPTORS:-lazy}
        export LD_LIBRARY_PATH __EGL_VENDOR_LIBRARY_DIRS MESA_LOADER_DRIVER_OVERRIDE ZINK_DESCRIPTORS
        unset GALLIUM_DRIVER
        ;;
    gles2|gles3)
        # The GLES trees moved from /opt to /home/.system/fgfs in
        # fgfs-sailfish-gles-2020.3.19-9: they are 793 MB and the root
        # filesystem has under 2 GB free.  /opt is still accepted so that a
        # system with the new starter and the old runtime keeps working.
        fgtree() {
            for base in /home/.system/fgfs /opt; do
                [ -d "$base/$1" ] && { echo "$base/$1"; return; }
            done
            echo "/home/.system/fgfs/$1"      # for the error message below
        }
        if [ "$BACKEND" = gles2 ]; then
            PREFIX=$(fgtree fgfs-gles); OSGDIR=$(fgtree osg-gles)
        else
            PREFIX=$(fgtree fgfs-gles3); OSGDIR=$(fgtree osg-gles3)
        fi
        LD_LIBRARY_PATH="$(libdirs "$OSGDIR" "$PREFIX")"
        OSG_LIBRARY_PATH="$(plugindir "$OSGDIR" "$PREFIX")"
        # the device EGL stack has no dma-heap we could import from
        FGFS_DMA_HEAP=${FGFS_DMA_HEAP:-/dev/null}
        # Vertex array objects.  Draw is CPU bound on per-drawable attribute
        # setup, not on fill rate: rendering a quarter of the pixels leaves
        # draw unchanged, so the cost is per draw call, and there are about
        # a thousand of those per frame.  A VAO carries that setup once
        # instead of on every call.  Measured on LOWW with the c172p,
        # threaded, 150 s settling: frame 87.5 -> 72.4 ms, draw 44.4 -> 34.9.
        # VERTEX_BUFFER_OBJECT is the weaker variant (draw 43.0).
        # OSG_VERTEX_BUFFER_HINT overrides; NO_PREFERENCE restores the old
        # client-array behaviour.
        OSG_VERTEX_BUFFER_HINT=${OSG_VERTEX_BUFFER_HINT:-VERTEX_ARRAY_OBJECT}
        export LD_LIBRARY_PATH OSG_LIBRARY_PATH FGFS_DMA_HEAP OSG_VERTEX_BUFFER_HINT
        # Draw on its own thread: the update phase then overlaps with the
        # draw, measured 84 -> 47 ms per frame.  FGFS_THREADING overrides.
        set -- "$@" "--prop:/sim/rendering/multithreading-mode=${FGFS_THREADING:-DrawThreadPerContext}"
        # 60 Hz flight model: plenty for light aircraft, half the JSBSim
        # work.  FGFS_MODEL_HZ overrides for aircraft that need more.
        set -- "$@" "--prop:/sim/model-hz=${FGFS_MODEL_HZ:-60}"
        # Random vegetation off by default.  A census of one frame's
        # drawables found 2404 of about 4000 carrying deciduous-alt.png or
        # mixed-alt.png - roughly 60 percent of every frame is trees - and
        # draw is bound by the number of calls, not by fill rate.  Measured
        # on LOWW with the c172p: frame 68.5 -> 45.3 ms, draw 33.7 -> 18.7,
        # 14.6 -> 22.1 fps.  For comparison, switching the clouds off is
        # worth 2.4 ms, because they are only 5 percent of the drawables.
        # FGFS_TREES=true puts them back.
        set -- "$@" "--prop:/sim/rendering/random-vegetation=${FGFS_TREES:-false}"
        ;;
    *)
        echo "fgfs-run: unbekanntes Backend '$BACKEND' - siehe --list-backends" >&2
        exit 2 ;;
esac

if [ ! -x "$PREFIX/bin/fgfs" ]; then
    echo "fgfs-run: $PREFIX/bin/fgfs fehlt - ist das Paket fuer '$BACKEND' installiert?" >&2
    exit 3
fi

EGL_PLATFORM=wayland
XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-/run/display}
WAYLAND_DISPLAY=${WAYLAND_DISPLAY:-wayland-0}
FGFS_SHM=${FGFS_SHM:-1}
export EGL_PLATFORM XDG_RUNTIME_DIR WAYLAND_DISPLAY FGFS_SHM

echo "fgfs-run: Backend $BACKEND ($PREFIX)"
exec "$PREFIX/bin/fgfs" --fg-root="$FGROOT" "$@"
