#!/bin/sh
# SPDX-License-Identifier: MIT
# enne2-cpufreq — helper privilegiato a interfaccia ristretta.
#
# Installato come /usr/local/libexec/enne2-cpufreq/cpufreq-helper (root:root 0755),
# invocato dal plasmoide org.enne2.cpufreq tramite pkexec e una azione polkit
# dedicata (org.enne2.cpufreq.set). Non accetta comandi arbitrari: solo i
# sottocomandi elencati in usage() e con argomenti validati contro sysfs.
#
# Uscita: righe key=value; ok=1 in caso di successo, ok=0 + error=<msg> + exit 2
# in caso di rifiuto. Ogni scrittura viene riletta per verifica.
set -u

PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH
umask 022

CMD=${1:-status}
[ "$#" -gt 0 ] && shift

SELF_DIR=$(dirname "$0")
if [ -r "$SELF_DIR/profiles.sh" ]; then
    # shellcheck source=../../contents/scripts/profiles.sh
    . "$SELF_DIR/profiles.sh"
else
    printf 'ok=0\nerror=profiles.sh mancante accanto all helper\n'
    exit 2
fi

CPUFREQ=/sys/devices/system/cpu/cpufreq
INTEL_PSTATE=/sys/devices/system/cpu/intel_pstate
STATE_DIR=/var/lib/enne2-cpufreq
BASELINE=$STATE_DIR/baseline
REFCOUNT=$STATE_DIR/refcount

usage() {
    cat <<'EOF'
uso: cpufreq-helper <comando> [argomenti]
  status                     stato corrente (leggibile anche senza root)
  set-governor <governor>    powersave | performance | ...
  set-epp <preferenza>       default | performance | balance_performance | balance_power | power
  set-limits <min_khz> <max_khz>
  set-boost <0|1>
  apply <profilo>            quiet | powersave | balanced | performance
  reset                      ripristina i default hardware (boost on, nessun tetto)
  save-state                 salva lo stato corrente come baseline
  restore-state              ripristina la baseline salvata
  task-begin <profilo>       salva la baseline (se serve) e applica il profilo
  task-end [--all]           decrementa il contatore e ripristina la baseline a zero
EOF
}

fail() {
    printf 'ok=0\nerror=%s\n' "$*"
    exit 2
}

readf() {                                  # readf <file> -> valore o exit 1
    [ -r "$1" ] || return 1
    _val=
    IFS= read -r _val < "$1" 2>/dev/null || [ -n "$_val" ] || return 1
    printf '%s\n' "$_val"
}

policies() {
    for _p in "$CPUFREQ"/policy[0-9]*; do
        [ -d "$_p" ] && printf '%s\n' "$_p"
    done
}

write_one() {                              # write_one <file> <valore>
    [ -w "$1" ] || return 1
    printf '%s\n' "$2" > "$1" 2>/dev/null
}

# Scrive un attributo su TUTTE le policy. Imposta WROTE e FAILED.
write_all() {
    _attr=$1
    _val=$2
    WROTE=0
    FAILED=0
    for _p in $(policies); do
        if write_one "$_p/$_attr" "$_val"; then
            WROTE=$((WROTE + 1))
        else
            FAILED=$((FAILED + 1))
        fi
    done
    [ "$FAILED" -eq 0 ] && [ "$WROTE" -gt 0 ]
}

# Valori distinti letti per un attributo, spazio-separati.
attr_values() {
    for _p in $(policies); do
        readf "$_p/$1"
    done | sort -u | tr '\n' ' ' | sed 's/ *$//'
}

p0=$(policies | head -n 1)
[ -n "$p0" ] || fail "nessuna policy cpufreq in $CPUFREQ"

driver=$(readf "$p0/scaling_driver" || echo unknown)
hw_min=$(readf "$p0/cpuinfo_min_freq" || echo 0)
hw_max=$(cf_hw_max_khz "$p0")   # indipendente dallo stato del boost
floor=$(readf "$p0/amd_pstate_lowest_nonlinear_freq" || echo "")
[ -n "$floor" ] && [ "$floor" -gt "$hw_min" ] || floor=$hw_min

boost_global=$CPUFREQ/boost
[ -w "$boost_global" ] || [ -r "$boost_global" ] || boost_global=""

boost_read() {
    for _p in $(policies); do
        readf "$_p/boost" && return 0
    done
    [ -n "$boost_global" ] && readf "$boost_global" && return 0
    readf "$INTEL_PSTATE/no_turbo" >/dev/null 2>&1 || return 1
    _nt=$(readf "$INTEL_PSTATE/no_turbo") || return 1
    [ "$_nt" = "1" ] && echo 0 || echo 1
}

print_state() {
    printf 'driver=%s\n' "$driver"
    printf 'mode=%s\n' "$(readf /sys/devices/system/cpu/amd_pstate/status || echo n/a)"
    printf 'governor=%s\n' "$(attr_values scaling_governor)"
    printf 'epp=%s\n' "$(attr_values energy_performance_preference)"
    printf 'min_khz=%s\n' "$(readf "$p0/scaling_min_freq")"
    printf 'max_khz=%s\n' "$(readf "$p0/scaling_max_freq")"
    printf 'hw_min_khz=%s\n' "$hw_min"
    printf 'hw_max_khz=%s\n' "$hw_max"
    printf 'floor_khz=%s\n' "$floor"
    printf 'boost=%s\n' "$(boost_read || echo n/a)"
    printf 'policies=%s\n' "$(policies | wc -l | tr -d ' ')"
    printf 'baseline=%s\n' "$([ -r "$BASELINE" ] && echo present || echo absent)"
    printf 'refcount=%s\n' "$([ -r "$REFCOUNT" ] && readf "$REFCOUNT" || echo 0)"
}

set_governor() {
    _g=${1:-}
    [ -n "$_g" ] || fail "governor mancante"
    case "$_g" in *[!a-z_-]*) fail "governor non valido: $_g" ;; esac
    _avail=$(readf "$p0/scaling_available_governors") || fail "governor non leggibili"
    case " $_avail " in
        *" $_g "*) ;;
        *) fail "governor '$_g' non disponibile ($_avail)" ;;
    esac
    write_all scaling_governor "$_g" || fail "scrittura scaling_governor fallita su $FAILED policy"
    _now=$(attr_values scaling_governor)
    [ "$_now" = "$_g" ] || fail "verifica governor fallita: letto '$_now'"
    printf 'governor=%s\n' "$_g"
}

set_epp() {
    _e=${1:-}
    [ -n "$_e" ] || fail "preferenza EPP mancante"
    case "$_e" in *[!a-z_]*) fail "preferenza EPP non valida: $_e" ;; esac
    _avail=$(readf "$p0/energy_performance_available_preferences") \
        || fail "questo driver non espone energy_performance_preference"
    case " $_avail " in
        *" $_e "*) ;;
        *) fail "EPP '$_e' non disponibile ($_avail)" ;;
    esac
    if write_all energy_performance_preference "$_e"; then
        printf 'epp=%s\n' "$_e"
        return 0
    fi
    # Con governor "performance" il driver EPP può ignorare la preferenza: non è
    # un errore fatale, viene segnalato come warning.
    if [ "$(attr_values scaling_governor)" = "performance" ]; then
        printf 'warning=EPP non applicato con governor performance\n'
        return 0
    fi
    fail "scrittura EPP fallita su $FAILED policy"
}

set_boost() {
    _b=${1:-}
    case "$_b" in 0|1) ;; *) fail "boost deve essere 0 o 1" ;; esac
    _count=0
    for _p in $(policies); do
        if [ -w "$_p/boost" ] && write_one "$_p/boost" "$_b"; then
            _count=$((_count + 1))
        fi
    done
    if [ -n "$boost_global" ] && write_one "$boost_global" "$_b"; then
        _count=$((_count + 1))
    fi
    if [ -w "$INTEL_PSTATE/no_turbo" ] && write_one "$INTEL_PSTATE/no_turbo" $((1 - _b)); then
        _count=$((_count + 1))
    fi
    [ "$_count" -gt 0 ] || fail "nessun controllo boost scrivibile su questo sistema"
    _now=$(boost_read || echo "")
    [ "$_now" = "$_b" ] || printf 'warning=lettura boost dopo la scrittura: %s\n' "${_now:-n/d}"
    printf 'boost=%s\n' "$_b"
}

set_limits() {
    _min=${1:-}
    _max=${2:-}
    case "$_min$_max" in *[!0-9]*) fail "limiti non numerici (kHz interi)" ;; esac
    [ -n "$_min" ] && [ -n "$_max" ] || fail "servono min_khz e max_khz"
    [ "$_min" -ge "$hw_min" ] || fail "minimo $_min sotto il limite hardware $hw_min"
    [ "$_max" -le "$hw_max" ] || fail "massimo $_max sopra il limite hardware $hw_max"
    [ "$_min" -le "$_max" ] || fail "minimo $_min maggiore del massimo $_max"
    _cur_min=$(readf "$p0/scaling_min_freq" || echo "$hw_min")
    _cur_max=$(readf "$p0/scaling_max_freq" || echo "$hw_max")
    # Ordine di scrittura sicuro: mai uno stato intermedio con min > max.
    if [ "$_min" -gt "$_cur_max" ]; then
        write_all scaling_max_freq "$_max" || fail "pre-allargamento del tetto fallito"
    fi
    if [ "$_max" -lt "$_cur_min" ]; then
        write_all scaling_min_freq "$_min" || fail "pre-abbassamento del minimo fallito"
    fi
    write_all scaling_min_freq "$_min" || fail "scrittura scaling_min_freq fallita su $FAILED policy"
    write_all scaling_max_freq "$_max" || fail "scrittura scaling_max_freq fallita su $FAILED policy"
    printf 'min_khz=%s\n' "$(readf "$p0/scaling_min_freq")"
    printf 'max_khz=%s\n' "$(readf "$p0/scaling_max_freq")"
}

apply_profile() {
    _pr=${1:-}
    cf_profile_known "$_pr" || fail "profilo '$_pr' sconosciuto ($(cf_profiles | tr '\n' ' '))"
    _gov=$(cf_profile_gov "$_pr")
    _epp=$(cf_profile_epp "$_pr")
    _bst=$(cf_profile_boost "$_pr")
    _maxf=$(cf_profile_max_khz "$_pr" "$floor" "$hw_max")
    [ "$_maxf" -gt "$hw_max" ] && _maxf=$hw_max
    [ "$_maxf" -lt "$floor" ] && _maxf=$floor
    printf 'profile=%s\n' "$_pr"
    # Ordine: 1) boost, 2) governor, 3) limiti, 4) EPP.
    # Il boost viene per primo perché con boost disattivato il kernel riduce il
    # tetto nominale e una scrittura di scaling_max_freq oltre quel valore
    # verrebbe rifiutata o limitata.
    set_boost "$_bst" || return 1
    if readf "$p0/scaling_available_governors" | grep -qw -- "$_gov"; then
        set_governor "$_gov" || return 1
    else
        printf 'warning=governor %s non disponibile, mantenuto %s\n' \
            "$_gov" "$(attr_values scaling_governor)"
    fi
    set_limits "$floor" "$_maxf" || return 1
    if readf "$p0/energy_performance_available_preferences" >/dev/null 2>&1; then
        set_epp "$_epp" || return 1
    fi
    printf 'profile_label=%s\n' "$(cf_profile_label "$_pr")"
}

ensure_state_dir() {
    if [ ! -d "$STATE_DIR" ]; then
        mkdir -p "$STATE_DIR" || fail "impossibile creare $STATE_DIR"
        chmod 755 "$STATE_DIR"
    fi
}

save_state() {
    ensure_state_dir
    {
        printf 'governor=%s\n' "$(readf "$p0/scaling_governor")"
        printf 'epp=%s\n' "$(readf "$p0/energy_performance_preference" || echo "")"
        printf 'min_khz=%s\n' "$(readf "$p0/scaling_min_freq")"
        printf 'max_khz=%s\n' "$(readf "$p0/scaling_max_freq")"
        printf 'boost=%s\n' "$(boost_read || echo "")"
        printf 'saved_at=%s\n' "$(date +%s)"
    } > "$BASELINE" || fail "scrittura baseline fallita"
    chmod 644 "$BASELINE"
    printf 'baseline=saved\n'
    cat "$BASELINE"
}

restore_state() {
    [ -r "$BASELINE" ] || fail "nessuna baseline salvata"
    _gov=$(sed -n 's/^governor=//p' "$BASELINE")
    _epp=$(sed -n 's/^epp=//p' "$BASELINE")
    _min=$(sed -n 's/^min_khz=//p' "$BASELINE")
    _max=$(sed -n 's/^max_khz=//p' "$BASELINE")
    _bst=$(sed -n 's/^boost=//p' "$BASELINE")
    if [ -n "$_gov" ]; then
        if readf "$p0/scaling_available_governors" | grep -qw -- "$_gov"; then
            write_all scaling_governor "$_gov" || printf 'warning=governor non ripristinato\n'
        fi
    fi
    if [ -n "$_min" ] && [ -n "$_max" ]; then
        case "$_min$_max" in
            *[!0-9]*) ;;
            *) set_limits "$_min" "$_max" || printf 'warning=limiti non ripristinati\n' ;;
        esac
    fi
    if [ -n "$_epp" ] && readf "$p0/energy_performance_available_preferences" >/dev/null 2>&1; then
        write_all energy_performance_preference "$_epp" || printf 'warning=EPP non ripristinato\n'
    fi
    if [ -n "$_bst" ]; then
        case "$_bst" in
            0|1) set_boost "$_bst" >/dev/null || printf 'warning=boost non ripristinato\n' ;;
        esac
    fi
    printf 'restored=1\n'
    print_state
}

reset_cmd() {
    _gov=powersave
    if ! readf "$p0/scaling_available_governors" | grep -qw -- "$_gov"; then
        _gov=$(readf "$p0/scaling_available_governors" | awk '{print $1}')
    fi
    printf 'profile=reset\n'
    [ -n "$_gov" ] && set_governor "$_gov"
    set_limits "$floor" "$hw_max" || return 1
    if readf "$p0/energy_performance_available_preferences" >/dev/null 2>&1; then
        _epp=balance_performance
        if ! readf "$p0/energy_performance_available_preferences" | grep -qw -- "$_epp"; then
            _epp=default
        fi
        set_epp "$_epp" || return 1
    fi
    set_boost 1 || return 1
}

task_begin() {
    _pr=${1:-}
    cf_profile_known "$_pr" || fail "profilo '$_pr' sconosciuto"
    ensure_state_dir
    _rc=0
    [ -r "$REFCOUNT" ] && _rc=$(readf "$REFCOUNT") 
    case "${_rc:-0}" in *[!0-9]*|"") _rc=0 ;; esac
    if [ "$_rc" -eq 0 ] || [ ! -r "$BASELINE" ]; then
        save_state >/dev/null || fail "salvataggio dello stato corrente fallito"
    fi
    apply_profile "$_pr" || fail "applicazione del profilo '$_pr' fallita"
    _rc=$((_rc + 1))
    printf '%s\n' "$_rc" > "$REFCOUNT"
    chmod 644 "$REFCOUNT"
    printf 'refcount=%s\n' "$_rc"
}

task_end() {
    ensure_state_dir
    _rc=0
    [ -r "$REFCOUNT" ] && _rc=$(readf "$REFCOUNT")
    case "${_rc:-0}" in *[!0-9]*|"") _rc=0 ;; esac
    if [ "${1:-}" = "--all" ]; then
        _rc=0
    else
        _rc=$((_rc - 1))
        [ "$_rc" -lt 0 ] && _rc=0
    fi
    printf '%s\n' "$_rc" > "$REFCOUNT"
    chmod 644 "$REFCOUNT"
    if [ "$_rc" -eq 0 ] && [ -r "$BASELINE" ]; then
        restore_state >/dev/null || printf 'warning=ripristino della baseline fallito\n'
        rm -f "$BASELINE"
        printf 'restored=1\n'
    fi
    printf 'refcount=%s\n' "$_rc"
}

case "$CMD" in
    status)
        print_state
        ;;
    usage|-h|--help|help)
        usage
        ;;
    set-governor)
        [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec"
        set_governor "${1:-}"
        ;;
    set-epp)
        [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec"
        set_epp "${1:-}"
        ;;
    set-limits)
        [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec"
        set_limits "${1:-}" "${2:-}"
        ;;
    set-boost)
        [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec"
        set_boost "${1:-}"
        ;;
    apply)
        [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec"
        apply_profile "${1:-}"
        ;;
    reset)
        [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec"
        reset_cmd
        ;;
    save-state)
        [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec"
        save_state
        ;;
    restore-state)
        [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec"
        restore_state
        ;;
    task-begin)
        [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec"
        task_begin "${1:-}"
        ;;
    task-end)
        [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec"
        task_end "${1:-}"
        ;;
    *)
        printf 'ok=0\nerror=comando sconosciuto: %s\n' "$CMD"
        usage
        exit 2
        ;;
esac

printf 'ok=1\n'
exit 0
