#!/bin/sh

LOG_TAG="BluetoothManager"
DBUS_INIT=/etc/init.d/dbus-1
BLUETOOTH_INIT=/etc/init.d/bluetooth
DBUS_WAS_STARTED=0
RETRY_SECONDS=30
PLAYBACK_PIDFILE=/var/run/aplay.pid
CAPTURE_PIDFILE=/var/run/btaudio-arecord.pid
STREAM_TOKENFILE=/var/run/btaudio-stream.id

# BTInit and Enigma2 can request a connection at the same time. Children must
# close this descriptor so playback does not keep the setup lock held.
exec 9>/var/run/btaudio-connect.lock
flock -w 10 9 || exit 1

log() {
    logger -t "$LOG_TAG" "$1"
}

pcm_process_running() {
    case "$1" in
        ''|*[!0-9]*|0|1) return 1 ;;
    esac
    [ -r "/proc/$1/cmdline" ] || return 1
    # A stale PID file must not cause us to signal an unrelated process.
    tr '\000' '\n' < "/proc/$1/cmdline" | grep -Fxq -- "$2"
}

stop_pcm() {
    pcm_pid=$(cat "$1" 2>/dev/null)
    if pcm_process_running "$pcm_pid" "$1"; then
        log "Stopping audio process (PID: $pcm_pid)"
        kill "$pcm_pid" 2>/dev/null
        for stop_wait in 1 2 3; do
            pcm_process_running "$pcm_pid" "$1" || break
            sleep 1
        done
        if pcm_process_running "$pcm_pid" "$1"; then
            kill -KILL "$pcm_pid" 2>/dev/null
        fi
    fi
    rm -f "$1"
}

capture_audio() {
    # The BCM capture driver may return EFAULT while the first service is
    # starting. Keep the playback pipe open and reopen capture in that case.
    # This is bounded startup recovery, not a permanent polling watchdog.
    capture_attempt=0
    while :; do
        [ "$(cat "$STREAM_TOKENFILE" 2>/dev/null)" = "$1" ] || return 0
        arecord -q -f dat -t raw --process-id-file "$CAPTURE_PIDFILE"
        capture_status=$?
        [ "$capture_status" -eq 1 ] || return "$capture_status"
        capture_attempt=$((capture_attempt+1))
        if [ "$capture_attempt" -ge 10 ]; then
            log "Audio capture failed repeatedly; stopping the stream"
            return 1
        fi
        playback_pid=$(cat "$PLAYBACK_PIDFILE" 2>/dev/null)
        pcm_process_running "$playback_pid" "$PLAYBACK_PIDFILE" || return 0
        [ "$(cat /proc/stb/audio/btaudio 2>/dev/null)" = on ] || return 0
        log "Audio capture not ready; retry $capture_attempt/9"
        sleep 1
        # Do not revive capture after the user stopped or replaced playback.
        [ "$(cat "$STREAM_TOKENFILE" 2>/dev/null)" = "$1" ] || return 0
        pcm_process_running "$playback_pid" "$PLAYBACK_PIDFILE" || return 0
        [ "$(cat /proc/stb/audio/btaudio 2>/dev/null)" = on ] || return 0
    done
}

dbus_ready() {
    [ -S /run/dbus/system_bus_socket ] || return 1
    dbus-send --system --dest=org.freedesktop.DBus --type=method_call --print-reply / org.freedesktop.DBus.ListNames >/dev/null 2>&1
}

start_dbus() {
    dbus_ready && return 0
    DBUS_WAS_STARTED=1
    [ -x "$DBUS_INIT" ] && "$DBUS_INIT" start >/dev/null 2>&1 || true
    i=0
    while [ $i -lt $RETRY_SECONDS ]; do
        dbus_ready && return 0
        sleep 1
        i=$((i+1))
    done
    log "D-Bus system bus not ready"
    return 1
}

start_bluetoothd() {
    if [ "$DBUS_WAS_STARTED" = "1" ] && pidof bluetoothd >/dev/null 2>&1; then
        "$BLUETOOTH_INIT" restart >/dev/null 2>&1 || true
        return 0
    fi
    pidof bluetoothd >/dev/null 2>&1 && return 0
    [ -x "$BLUETOOTH_INIT" ] && "$BLUETOOTH_INIT" start >/dev/null 2>&1 || true
    i=0
    while [ $i -lt $RETRY_SECONDS ]; do
        pidof bluetoothd >/dev/null 2>&1 && return 0
        sleep 1
        i=$((i+1))
    done
    log "bluetoothd not running"
    return 1
}

log "Script started"

default_asoundconf() {
cat << EOF > /etc/asound.conf
pcm.!default {
    type plug
    slave.pcm "hw:0"
}

ctl.mixer0 {
    type hw
    card 0
}
EOF
log "Reset /etc/asound.conf to default configuration"
}

bluetooth_asoundconf() {
cat << EOF >> /etc/asound.conf
pcm.bluetooth {
    type plug
    slave.pcm {
        type bluealsa
        interface "hci0"
        device "$1"
        profile "a2dp"
    }
    hint {
        show on
        description "Bluetooth $2"
    }
}

ctl.bluetooth {
    type bluealsa
}
EOF
log "Added Bluetooth device '$2' ($1) to /etc/asound.conf"
}

rm -f "$STREAM_TOKENFILE"
stop_pcm "$PLAYBACK_PIDFILE"
stop_pcm "$CAPTURE_PIDFILE"

log "Applying default sound configuration"
default_asoundconf

if [ -n "$1" ]; then
    start_dbus || exit 1
    start_bluetoothd || exit 1
    bluetoothaddr="$1"
    log "Bluetooth address provided: $bluetoothaddr"

    if ! bluetoothctl info "$bluetoothaddr" >/dev/null 2>&1; then
        log "Error: Bluetooth device $bluetoothaddr not found"
        exit 1
    fi

    log "Connecting to Bluetooth device: $bluetoothaddr"
    if ! timeout 20 bluetoothctl connect "$bluetoothaddr"; then
        log "Error: Could not connect to Bluetooth device $bluetoothaddr"
        exit 1
    fi

    if ! bluetoothctl info "$bluetoothaddr" | grep -q 'Connected: yes'; then
        log "Error: Bluetooth device $bluetoothaddr is not connected"
        exit 1
    fi
    
    if ! bluetoothctl info "$bluetoothaddr" | grep -q "Audio Sink"; then
        log "Error: No Audio Sink profile found for $bluetoothaddr"
        exit 1
    fi

    bluetoothname="$(bluetoothctl info "$bluetoothaddr" | grep "Name" | cut -d ':' -f 2 | xargs)"
    log "Device name detected: $bluetoothname"

    bluetooth_asoundconf "$bluetoothaddr" "$bluetoothname"

    log "Starting audio streaming to Bluetooth device"
    printf '%s\n' "$$" > "$STREAM_TOKENFILE"
    capture_audio "$$" 9>&- | aplay -q -f dat --buffer-size=16384 --period-size=2048 -t raw -D bluetooth --process-id-file "$PLAYBACK_PIDFILE" 9>&- &
else
    log "No Bluetooth address provided, exiting"
fi

log "Script finished"
exit 0
