#!/bin/sh
set -eu

active_config=${PRISM_PTP_ACTIVE_CONFIG:-/etc/linuxptp/prism-ptp4l.conf}
holdover_config=${PRISM_PTP_HOLDOVER_CONFIG:-/etc/linuxptp/prism-ptp4l-ptp.conf}
gnss_config=${PRISM_PTP_GNSS_CONFIG:-/etc/linuxptp/prism-ptp4l-gnss.conf}
host_config=${PRISM_PTP_HOST_CONFIG:-/etc/linuxptp/prism-ptp4l-host.conf}
state_file=${PRISM_TIME_SOURCE_STATE_FILE:-/run/prism-time-source.mode}
sensor_board_source_file=${PRISM_SENSOR_BOARD_TIME_SOURCE_FILE:-/run/prism-sensor-board-time-source}
required_checks=${PRISM_TIME_SOURCE_REQUIRED_CHECKS:-3}
check_interval=${PRISM_TIME_SOURCE_CHECK_INTERVAL:-1}
max_checks=${PRISM_TIME_SOURCE_MAX_CHECKS:-0}
no_restart=${PRISM_TIME_SOURCE_NO_RESTART:-0}
chronyc_command=${PRISM_CHRONYC_COMMAND:-chronyc}
candidate_mode=
candidate_checks=0
checks=0

case "$(readlink "$active_config" 2>/dev/null || true)" in
    *prism-ptp4l-gnss.conf) mode=gnss ;;
    *prism-ptp4l-host.conf) mode=host ;;
    *) mode=holdover ;;
esac

pps_locked() {
    "$chronyc_command" -c sources 2>/dev/null |
        awk -F, '$1 == "#*" && $2 == "PPS" { found=1 } END { exit !found }'
}

select_mode() {
    new_mode=$1
    case "$new_mode" in
        gnss) target=$gnss_config ;;
        host) target=$host_config ;;
        holdover) target=$holdover_config ;;
        *) return 1 ;;
    esac

    ln -sfn "$target" "$active_config"
    printf '%s\n' "$new_mode" >"$state_file"
    if [ "$no_restart" != 1 ]; then
        logger -t prism-time-source "selected $new_mode timing mode"
        systemctl restart prism-ptp4l.service
        # PHC remains TAI in every quality mode; do not restart its servo.
    fi
    mode=$new_mode
}

# All modes keep RK as the PTP grandmaster. The internal Sensor Board timeline
# is a valid RK clock source but remains PTP holdover because it has no absolute
# UTC traceability. Only external GNSS may advertise GNSS traceability; Host UTC
# fallback keeps its own conservative profile.
printf '%s\n' "$mode" >"$state_file"
while :; do
    if pps_locked; then
        case "$(sed -n '1p' "$sensor_board_source_file" 2>/dev/null || true)" in
            gps) desired_mode=gnss ;;
            host) desired_mode=host ;;
            internal) desired_mode=holdover ;;
            *) desired_mode=holdover ;;
        esac
    else
        desired_mode=holdover
    fi
    if [ "$desired_mode" = "$candidate_mode" ]; then
        candidate_checks=$((candidate_checks + 1))
    else
        candidate_mode=$desired_mode
        candidate_checks=1
    fi
    if [ "$mode" != "$candidate_mode" ] &&
       [ "$candidate_checks" -ge "$required_checks" ]; then
        select_mode "$candidate_mode"
        candidate_checks=0
    fi
    checks=$((checks + 1))
    if [ "$max_checks" -gt 0 ] && [ "$checks" -ge "$max_checks" ]; then
        exit 0
    fi
    sleep "$check_interval"
done
