#!/usr/bin/env bash

VERBOSE=false

BIN_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
if [ -z "$SERPANTINUM_DIR" ]; then
    export SERPANTINUM_DIR="$(dirname "$BIN_DIR")/src"
fi

source "$SERPANTINUM_DIR/scripts/caching.sh"
source "$SERPANTINUM_DIR/scripts/config.sh"
source "$SERPANTINUM_DIR/scripts/i18n.sh"

if [ -z "$MAIN_QML" ]; then
    export MAIN_QML="$(find "$SERPANTINUM_DIR/quickshell" -type f \( -name "Shell.qml" -o -name "Bar.qml" \) 2>/dev/null | head -n 1)"
fi

is_alive() {
    local pid="$1"
    [ -n "$pid" ] || return 1
    kill -0 "$pid" 2>/dev/null || return 1
    local stat
    stat=$(<"/proc/$pid/stat") 2>/dev/null || return 1
    [[ "$stat" =~ \)\ Z ]] && return 1
    return 0
}

terminate_and_wait() {
    local pid="$1"
    local timeout="${2:-2}"
    local count=0
    local max_count=$(( timeout * 20 ))

    if [ -z "$pid" ] || ! is_alive "$pid"; then
        return 0
    fi

    kill -TERM "$pid" 2>/dev/null

    while is_alive "$pid"; do
        if [ "$count" -ge "$max_count" ]; then
            kill -KILL "$pid" 2>/dev/null
            break
        fi
        sleep 0.05
        ((count++))
    done
}

get_all_children() {
    local target="$1"
    local child
    for child in $(pgrep -P "$target" 2>/dev/null); do
        get_all_children "$child"
        echo "$child"
    done
}

get_installed_version() {
    local version_file="${XDG_STATE_HOME:-$HOME/.local/state}/serpantinum/version"
    local ver=""
    if [ -f "$version_file" ]; then
        ver=$(awk -F= '/^SERPANTINUM_VERSION=/{gsub(/["'\'' ]/, "", $2); print $2}' "$version_file")
    fi
    if [ -z "$ver" ] && [ -n "$SERPANTINUM_VERSION" ]; then
        ver="$SERPANTINUM_VERSION"
    fi
    if [ -z "$ver" ]; then
        if [ -n "$SERPANTINUM_DIR" ] && [ -f "$SERPANTINUM_DIR/version.txt" ]; then
            ver=$(cat "$SERPANTINUM_DIR/version.txt" 2>/dev/null | xargs)
        elif [ -f "$(dirname "$BIN_DIR")/version.txt" ]; then
            ver=$(cat "$(dirname "$BIN_DIR")/version.txt" 2>/dev/null | xargs)
        elif [ -n "$SERPANTINUM_DIR" ] && [ -f "$(dirname "$SERPANTINUM_DIR")/version.txt" ]; then
            ver=$(cat "$(dirname "$SERPANTINUM_DIR")/version.txt" 2>/dev/null | xargs)
        fi
    fi
    if [ -z "$ver" ]; then
        ver="2.0.0"
    fi
    echo "$ver"
}

show_help() {
    echo "$(t "daemon.cli.usage")"
    echo ""
    echo "$(t "daemon.cli.description")"
    echo ""
    echo "$(t "daemon.cli.commands")"
    echo "  start                       $(t "daemon.cli.cmd_start")"
    echo "  stop                        $(t "daemon.cli.cmd_stop")"
    echo "  status                      $(t "daemon.cli.cmd_status")"
    echo ""
    echo "$(t "daemon.cli.options")"
    echo "  -v, --verbose               $(t "daemon.cli.opt_verbose")"
    echo "  -V, --version               $(t "daemon.cli.opt_version")"
    echo "  -h, --help                  $(t "daemon.cli.opt_help")"
    echo ""
}

COMMAND=""
while [ $# -gt 0 ]; do
    case "$1" in
        -v|--verbose)
            VERBOSE=true
            export SERPANTINUM_VERBOSE=true
            shift
            ;;
        -V|--version)
            echo "serpantinumd v$(get_installed_version)"
            exit 0
            ;;
        -h|--help)
            show_help
            exit 0
            ;;
        start|stop|status)
            if [ -z "$COMMAND" ]; then
                COMMAND="$1"
            fi
            shift
            ;;
        *)
            echo "$(t "daemon.cli.unknown_arg" ARG="$1")"
            show_help
            exit 1
            ;;
    esac
done

if [ -z "$COMMAND" ]; then
    COMMAND="start"
fi

if [ "$COMMAND" == "stop" ]; then
    pid=""
    if [ -f "/tmp/serpantinumd.pid" ]; then
        pid="$(cat "/tmp/serpantinumd.pid" 2>/dev/null)"
    fi

    if [ -n "$pid" ] && is_alive "$pid"; then
        terminate_and_wait "$pid" 3
    else
        pkill -TERM -f "focus_daemon.py" 2>/dev/null
        if [ -n "$MAIN_QML" ]; then
            pkill -TERM -f "quickshell.*$MAIN_QML" 2>/dev/null
        fi
        sleep 0.2
    fi

    pkill -KILL -f "focus_daemon.py" 2>/dev/null
    if [ -n "$MAIN_QML" ]; then
        pkill -KILL -f "quickshell.*$MAIN_QML" 2>/dev/null
    fi
    pkill -TERM -f "wl-paste --watch echo 1" 2>/dev/null
    bash "$SERPANTINUM_DIR/scripts/blue_light_filter.sh" stop >/dev/null 2>&1

    rm -f "/tmp/serpantinumd.pid" 2>/dev/null
    rm -f "/tmp/serpantinumd.lock" 2>/dev/null
    echo "$(t "daemon.cli.stopped")"
    exit 0
fi

if [ "$COMMAND" == "status" ]; then
    pid=""
    if [ -f "/tmp/serpantinumd.pid" ]; then
        pid="$(cat "/tmp/serpantinumd.pid" 2>/dev/null)"
    fi
    if [ -n "$pid" ] && is_alive "$pid"; then
        echo "$(t "daemon.cli.status_running" PID="$pid")"
        exit 0
    else
        echo "$(t "daemon.cli.status_stopped")"
        exit 1
    fi
fi

# Hardware decoder for video wallpapers when the NVIDIA driver is loaded.
# FFmpeg's VAAPI (as used by Qt) opens the FIRST render node, so VAAPI is only
# chosen when that node belongs to another GPU (hybrid laptops: decode on the
# iGPU and let the NVIDIA GPU sleep). Otherwise decode on the CPU.
wallpaper_decoder() {
    local drm="${1:-/sys/class/drm}" node
    for node in "$drm"/renderD*; do
        [ -r "$node/device/vendor" ] || continue
        if [ "$(cat "$node/device/vendor")" != "0x10de" ]; then
            echo vaapi
        else
            echo none
        fi
        return
    done
    echo none
}

if [ "$COMMAND" == "start" ]; then
    exec 9>"/tmp/serpantinumd.lock"

    if ! flock -n 9; then
        existing_pid=""
        [ -f "/tmp/serpantinumd.pid" ] && existing_pid="$(cat "/tmp/serpantinumd.pid" 2>/dev/null)"

        if [ -n "$existing_pid" ] && is_alive "$existing_pid"; then
            echo "$(t "daemon.already_running")"
            exit 1
        fi

        pkill -KILL -f "focus_daemon.py" 2>/dev/null
        if [ -n "$MAIN_QML" ]; then
            pkill -KILL -f "quickshell.*$MAIN_QML" 2>/dev/null
        fi
        sleep 0.2

        if ! flock -n 9; then
            echo "$(t "daemon.already_running")"
            exit 1
        fi
    fi

    echo $$ > "/tmp/serpantinumd.pid"

    CLEANUP_DONE=false
    LAUNCH_PID=""
    QS_PID=""

    cleanup() {
        if [ "$CLEANUP_DONE" = true ]; then
            return
        fi
        CLEANUP_DONE=true
        trap '' SIGINT SIGTERM SIGHUP

        [ "$VERBOSE" = true ] && echo "$(t "daemon.shutting_down")"

        local group
        for group in "$QS_PID" "$LAUNCH_PID"; do
            [ -n "$group" ] && kill -TERM -- "-$group" 2>/dev/null
        done
        pkill -TERM -f "focus_daemon.py" 2>/dev/null

        local count=0
        while [ "$count" -lt 20 ]; do
            if ! is_alive "$QS_PID" && ! is_alive "$LAUNCH_PID"; then
                break
            fi
            sleep 0.05
            ((count++))
        done

        for group in "$QS_PID" "$LAUNCH_PID"; do
            [ -n "$group" ] && kill -KILL -- "-$group" 2>/dev/null
        done
        pkill -KILL -f "focus_daemon.py" 2>/dev/null
        bash "$SERPANTINUM_DIR/scripts/blue_light_filter.sh" stop 9>&- >/dev/null 2>&1

        exec 9>&- 2>/dev/null
        rm -f "/tmp/serpantinumd.pid" 2>/dev/null
        rm -f "/tmp/serpantinumd.lock" 2>/dev/null
        exit 0
    }

    trap cleanup SIGINT SIGTERM SIGHUP EXIT

    [ "$VERBOSE" = true ] && echo "$(t "daemon.launching")"

    (
        bash "$SERPANTINUM_DIR/scripts/migrate_easyeffects.sh"
        command -v serpantinum-audio >/dev/null 2>&1 && serpantinum-audio start
    ) 9>&- >/dev/null 2>&1 &

    setsid bash "$SERPANTINUM_DIR/quickshell/guide/wellbeing/launch_daemon.sh" 9>&- &
    LAUNCH_PID=$!

    # On NVIDIA, keep wallpaper video decoding off the NVIDIA GPU: NVDEC keeps it at
    # full clocks (~+8 W measured on a 1080p loop). Users can still override the variable.
    if [ -d /proc/driver/nvidia ]; then
        export QT_FFMPEG_DECODING_HW_DEVICE_TYPES="${QT_FFMPEG_DECODING_HW_DEVICE_TYPES-$(wallpaper_decoder)}"
    fi
    setsid quickshell -p "$MAIN_QML" 9>&- &
    QS_PID=$!

    while kill -0 "$QS_PID" 2>/dev/null; do
        wait "$QS_PID" 2>/dev/null
    done
fi
