#!/bin/bash
# =============================================================================
# bookworm-fix-audio-orangepi6plus.sh  (v1.1)
#
# Corrige l'audio analogique (jack headphone 3.5mm) sur OrangePi 6 Plus
# en contournant un bug du BIOS CIX qui ne configure pas le pin 0x15
# du codec Realtek ALC269VC au démarrage.
#
# Ce script :
#   1. Sauvegarde les anciennes configs PipeWire en .bk-<date>
#   2. Installe une config PipeWire utilisant hw:0,0 comme sink par défaut
#   3. Crée un service systemd qui au boot :
#        - configure le pin 0x15 (headphone) du codec ALC269VC
#        - démute le Master (muté par défaut sur ce hardware)
#        - met le volume à 80% et sauvegarde l'état ALSA
#   4. Applique la correction immédiatement sans reboot
#
# Hardware : OrangePi 6 Plus / CIX CD8180 / Realtek ALC269VC
# Target   : jack audio 3.5mm à côté du bouton d'alimentation
#
# Changelog :
#   v1.1 — Ajout unmute Master + volume 80% + alsactl store
#   v1.0 — Pin override + config PipeWire initial
#
# Kenny / BOOKWORM — https://www.bookworm-mon0.org
# =============================================================================

set -e

# --- Couleurs ---
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'

# --- Chemins et constantes ---
PW_CONF_DIR="/etc/pipewire/pipewire.conf.d"
PW_CONF_NEW="${PW_CONF_DIR}/99-orangepi-audio.conf"
PIN_OVERRIDE_SCRIPT="/usr/local/sbin/orangepi-alc269-pin-override.sh"
PIN_OVERRIDE_SERVICE="/etc/systemd/system/orangepi-alc269-pin-override.service"
BACKUP_SUFFIX=".bk-$(date +%Y%m%d-%H%M%S)"

# Pin config pour headphone sur ALC269VC node 0x15
# Décodage 0x02214020 :
#   [byte0=0x20] Sequence=0, Association=2, Misc=0
#   [byte1=0x40] Color=0 (unknown), Connector=2 (1/8 stereo)
#   [byte2=0x21] Location=1 (external rear), Geometric=2
#   [byte3=0x02] Default device=2 (Headphone Out)
ALC269_HP_PINCFG="0x02214020"
ALC269_HP_NODE="0x15"
ALC269_DEFAULT_VOLUME="80%"

# --- Helpers ---
step()  { echo -e "\n${BLUE}${BOLD}[STEP]${NC} $1"; }
ok()    { echo -e "  ${GREEN}✓${NC} $1"; }
warn()  { echo -e "  ${YELLOW}⚠${NC} $1"; }
fail()  { echo -e "  ${RED}✗${NC} $1"; }
info()  { echo -e "  ${CYAN}→${NC} $1"; }
die()   { echo -e "\n${RED}${BOLD}FATAL:${NC} $1\n"; exit 1; }

# =============================================================================
# BANNER
# =============================================================================
echo ""
echo -e "${BLUE}${BOLD}╔═══════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}${BOLD}║   BOOKWORM — OrangePi 6 Plus Audio Fix (v1.1)         ║${NC}"
echo -e "${BLUE}${BOLD}║   Headphone jack (ALC269VC pin 0x15) + PipeWire       ║${NC}"
echo -e "${BLUE}${BOLD}╚═══════════════════════════════════════════════════════╝${NC}"
echo ""

# =============================================================================
# VÉRIFICATIONS PRÉLIMINAIRES
# =============================================================================
step "Vérifications préliminaires"

# Root requis
[[ "$EUID" -ne 0 ]] && die "Ce script doit être lancé en root (sudo $0)"
ok "Privilèges root"

# Board check
if [[ -f /sys/firmware/devicetree/base/model ]]; then
    MODEL=$(tr -d '\0' < /sys/firmware/devicetree/base/model)
    if [[ "$MODEL" == *"Orange Pi 6 Plus"* ]]; then
        ok "Board détecté : $MODEL"
    else
        warn "Board détecté : $MODEL (ce script est conçu pour Orange Pi 6 Plus)"
        read -r -p "  Continuer quand même ? [o/N] " reply
        [[ ! "$reply" =~ ^[oOyY]$ ]] && die "Abandon"
    fi
else
    warn "Impossible de détecter le modèle du board (pas de device tree)"
fi

# Codec ALC269VC présent ?
if [[ ! -d /sys/class/sound/hwC0D0 ]]; then
    die "Codec HDA card0 introuvable (/sys/class/sound/hwC0D0 absent)"
fi
CODEC_NAME=$(cat /sys/class/sound/hwC0D0/chip_name 2>/dev/null || echo "inconnu")
if [[ "$CODEC_NAME" == *"ALC269"* ]]; then
    ok "Codec détecté : $CODEC_NAME"
else
    warn "Codec détecté : $CODEC_NAME (attendu : ALC269VC)"
fi

# Outils requis
for tool in amixer alsactl; do
    if ! command -v "$tool" >/dev/null 2>&1; then
        warn "Outil absent : $tool — install alsa-utils"
    fi
done

# PipeWire présent ?
if [[ ! -d "$PW_CONF_DIR" ]]; then
    info "Création du dossier $PW_CONF_DIR"
    mkdir -p "$PW_CONF_DIR"
fi
ok "Dossier PipeWire prêt"

# =============================================================================
# SAUVEGARDE DES ANCIENNES CONFIGS
# =============================================================================
step "Sauvegarde des configurations existantes"

BACKED_UP=0
for old_conf in "$PW_CONF_DIR"/99-dp-audio-fix.conf \
                "$PW_CONF_DIR"/99-dp-clock.conf \
                "$PW_CONF_DIR"/99-orangepi-audio.conf; do
    if [[ -f "$old_conf" ]]; then
        backup_name="${old_conf}${BACKUP_SUFFIX}"
        cp "$old_conf" "$backup_name"
        ok "Sauvegardé : $(basename $old_conf) → $(basename $backup_name)"
        rm -f "$old_conf"
        BACKED_UP=$((BACKED_UP + 1))
    fi
done
[[ $BACKED_UP -eq 0 ]] && info "Aucune ancienne config à sauvegarder"

# =============================================================================
# INSTALLATION — SCRIPT DE PIN OVERRIDE + UNMUTE
# =============================================================================
step "Installation du script de correction codec ALC269VC"

cat > "$PIN_OVERRIDE_SCRIPT" << 'PIN_EOF'
#!/bin/bash
# =============================================================================
# orangepi-alc269-pin-override.sh
#
# Applique au boot les corrections nécessaires au codec Realtek ALC269VC
# de l'OrangePi 6 Plus :
#   1. Configure le pin 0x15 comme Headphone Out (le BIOS CIX le laisse en N/A)
#   2. Démute le Master (muté par défaut sur ce hardware)
#   3. Met le volume à 80% et sauvegarde l'état ALSA
#
# Installé par bookworm-fix-audio-orangepi6plus.sh
# Appelé par orangepi-alc269-pin-override.service
# =============================================================================

set -u

SYSFS="/sys/class/sound/hwC0D0"
NODE="0x15"
PINCFG="0x02214020"
VOLUME="80%"

# Vérifier que le codec est présent
if [[ ! -d "$SYSFS" ]]; then
    echo "ERREUR: codec HDA hwC0D0 absent" >&2
    exit 1
fi

# Vérifier que c'est bien un ALC269
CHIP=$(cat "$SYSFS/chip_name" 2>/dev/null || echo "")
if [[ "$CHIP" != *"ALC269"* ]]; then
    echo "ERREUR: codec n'est pas un ALC269 (détecté: $CHIP)" >&2
    exit 1
fi

# --- 1. Pin override ---
CURRENT=$(grep "^$NODE " "$SYSFS/init_pin_configs" 2>/dev/null | awk '{print $2}')
if [[ "$CURRENT" == "$PINCFG" ]]; then
    echo "Pin $NODE déjà configuré à $PINCFG"
else
    echo "Application pin override : $NODE = $PINCFG"
    echo "$NODE $PINCFG" > "$SYSFS/user_pin_configs"
    echo 1 > "$SYSFS/reconfig"
    sleep 1
fi

# --- 2. Unmute Master + volume par défaut ---
# Le codec ALC269VC sur OrangePi 6 Plus démarre muté (MM dans alsamixer)
# On démute et on définit un volume audible
if command -v amixer >/dev/null 2>&1; then
    amixer -c 0 sset Master unmute >/dev/null 2>&1 || true
    amixer -c 0 sset Master "$VOLUME" >/dev/null 2>&1 || true
    echo "Master démuté à $VOLUME"
fi

# --- 3. Sauvegarder l'état ALSA pour persistance ---
if command -v alsactl >/dev/null 2>&1; then
    alsactl store 0 >/dev/null 2>&1 || true
    echo "État ALSA sauvegardé"
fi

exit 0
PIN_EOF

chmod +x "$PIN_OVERRIDE_SCRIPT"
ok "Script installé : $PIN_OVERRIDE_SCRIPT"

# =============================================================================
# INSTALLATION — SERVICE SYSTEMD (PERSISTANCE AU BOOT)
# =============================================================================
step "Installation du service systemd"

cat > "$PIN_OVERRIDE_SERVICE" << SYSTEMD_EOF
[Unit]
Description=OrangePi 6 Plus ALC269VC audio fix (BOOKWORM)
Documentation=https://bookworm.ddns.net
# Démarrer après que ALSA ait chargé le codec
After=sound.target systemd-modules-load.service
Wants=sound.target
# Avant les sessions utilisateur pour que PipeWire voie le bon état
Before=systemd-user-sessions.service

[Service]
Type=oneshot
ExecStart=$PIN_OVERRIDE_SCRIPT
RemainAfterExit=yes
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
SYSTEMD_EOF

ok "Service installé : $PIN_OVERRIDE_SERVICE"

# Activer au boot
systemctl daemon-reload
systemctl enable orangepi-alc269-pin-override.service >/dev/null 2>&1
ok "Service activé au boot"

# =============================================================================
# INSTALLATION — CONFIG PIPEWIRE
# =============================================================================
step "Installation de la configuration PipeWire"

cat > "$PW_CONF_NEW" << 'PW_EOF'
# =============================================================================
# OrangePi 6 Plus — Audio Headphone (ALC269VC)
#
# Force PipeWire à utiliser hw:0,0 (ALC269VC Analog) comme sink audio.
# Nécessaire car WirePlumber ne détecte pas automatiquement le device sur
# ce SoC CIX Sky1 (absence de règles ACPI/DT spécifiques).
#
# Le pin override et l'unmute Master sont appliqués par le service systemd
# orangepi-alc269-pin-override.service au boot.
#
# Installé par : bookworm-fix-audio-orangepi6plus.sh
# =============================================================================

context.properties = {
    default.clock.rate          = 48000
    default.clock.allowed-rates = [ 48000 ]
}

context.objects = [
    { factory = adapter
        args = {
            factory.name     = api.alsa.pcm.sink
            node.name        = "alsa-output-headphone"
            node.description = "OrangePi Headphone (ALC269VC)"
            media.class      = "Audio/Sink"
            api.alsa.path    = "hw:0,0"
            audio.channels   = 2
            audio.position   = [ FL FR ]

            # Stabilite ALSA sur ARM64
            api.alsa.period-size    = 4096
            api.alsa.headroom       = 1024
            api.alsa.disable-tsched = true

            # Anti-crepitement / verrouillage quantum
            node.lock-quantum       = true
            node.force-quantum      = 4096
            session.suspend-timeout-seconds = 0

            # Compatibilite
            api.alsa.open-ucm       = false
        }
    }
]
PW_EOF

ok "Config PipeWire installée : $PW_CONF_NEW"

# =============================================================================
# APPLICATION IMMÉDIATE (SANS REBOOT)
# =============================================================================
step "Application immédiate de la correction"

# 1. Appliquer le pin override + unmute maintenant
info "Exécution de $PIN_OVERRIDE_SCRIPT..."
if "$PIN_OVERRIDE_SCRIPT"; then
    ok "Correction codec appliquée"
else
    warn "Correction codec : vérification recommandée au reboot"
fi

# 2. Redémarrer PipeWire pour les utilisateurs actifs en session graphique
GRAPHICAL_USERS=$(who | awk '{print $1}' | sort -u)
if [[ -n "$GRAPHICAL_USERS" ]]; then
    for USER in $GRAPHICAL_USERS; do
        USER_UID=$(id -u "$USER" 2>/dev/null || echo "")
        [[ -z "$USER_UID" ]] && continue
        [[ "$USER_UID" -lt 1000 ]] && continue

        if [[ -d "/run/user/$USER_UID" ]]; then
            info "Redémarrage PipeWire pour $USER (UID $USER_UID)..."
            su - "$USER" -c "
                export XDG_RUNTIME_DIR=/run/user/$USER_UID
                export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$USER_UID/bus
                systemctl --user restart pipewire pipewire-pulse wireplumber 2>/dev/null
            " && ok "PipeWire redémarré pour $USER" || \
                warn "Impossible de redémarrer PipeWire pour $USER"
        fi
    done
else
    warn "Aucune session utilisateur active — PipeWire sera configuré au prochain login"
fi

# =============================================================================
# VÉRIFICATION FINALE
# =============================================================================
step "Vérification finale"

# Vérifier le pin override
USER_PIN=$(cat /sys/class/sound/hwC0D0/user_pin_configs 2>/dev/null || echo "")
if [[ -n "$USER_PIN" ]]; then
    ok "Pin override actif : $USER_PIN"
else
    warn "Pin override non visible dans user_pin_configs (sera appliqué au boot)"
fi

# Vérifier que le Master est démuté
MASTER_STATE=$(amixer -c 0 sget Master 2>/dev/null | grep -oE '\[on\]|\[off\]' | head -1)
if [[ "$MASTER_STATE" == "[on]" ]]; then
    ok "Master : démuté"
else
    warn "Master état : $MASTER_STATE"
fi

# =============================================================================
# RÉSUMÉ FINAL
# =============================================================================
echo ""
echo -e "${BLUE}${BOLD}╔═══════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}${BOLD}║   Correction audio appliquée !                        ║${NC}"
echo -e "${BLUE}${BOLD}╚═══════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e "  ${CYAN}Fichiers installés :${NC}"
echo -e "    • $PIN_OVERRIDE_SCRIPT"
echo -e "    • $PIN_OVERRIDE_SERVICE"
echo -e "    • $PW_CONF_NEW"
echo ""
BACKUPS=$(ls "$PW_CONF_DIR"/*${BACKUP_SUFFIX} 2>/dev/null || true)
if [[ -n "$BACKUPS" ]]; then
    echo -e "  ${CYAN}Sauvegardes créées :${NC}"
    for bk in $BACKUPS; do
        echo -e "    • $bk"
    done
    echo ""
fi
echo -e "  ${YELLOW}Test de l'audio :${NC}"
echo -e "    Brancher un casque sur le jack 3.5mm puis :"
echo -e "    ${CYAN}speaker-test -D hw:0,0 -c 2 -t sine -l 1 -f 440${NC}"
echo ""
echo -e "  ${YELLOW}Ou depuis la session GNOME :${NC}"
echo -e "    Paramètres → Son → Sortie → OrangePi Headphone (ALC269VC)"
echo ""
echo -e "  ${GREEN}${BOLD}BOOKWORM${NC} — https://www.bookworm-mon0.org 🎧"
echo ""
