#!/bin/bash
#
# Author   : Gaston Gonzalez
#          : Cliff Jones
# Date     : 1 January 2025
# Updated  : 17 March 2025
# Purpose  : Utility for accessing the Bluetooth TNC on the Vero VGC VR-N76.           
source /opt/emcomm-tools/bin/et-common

CLEAN_UP=1

cleanup() {
  # Note: We need to reconnect to the Bluetooth device on every mode change. 
  #       As such, we need kill all the rfcomm and kiss process on exit.
  if [[ ${CLEAN_UP} -eq 1 ]]; then
    sudo /opt/emcomm-tools/bin/et-kill-bt
  fi
}

# Kill all Bluetooth TNC services on exit
trap cleanup EXIT

ACTIVE_RADIO="${ET_HOME}/conf/radios.d/vgc-vrn76.bt.json"
DEVICE_NAME="VR-N76"
BT_DEVICE="/dev/rfcomm0"
AX25_PORT="wl2k"
AX25_CONF_FILE="/etc/ax25/axports"

function usage() {
  echo "Usage: $(basename $0) <command>"
  echo "  c, connect - Connect to radio via Bluetooth" 
  echo "  p, pair    - Pair radio (one-time operation)" 
  echo "  u, unpair  - Unpair radio" 

  CLEAN_UP=0
}

if [[ $# -ne 1 ]]; then
  usage
  exit 1
fi

if [[ -e ${BT_DEVICE} ]]; then
  echo -e "${YELLOW}Your ${BLUE}${DEVICE_NAME}${YELLOW} may have a connection that needs to be cleaned up."
  echo -e "Press ${WHITE}[ENTER]${YELLOW} to clean-up.${NC}"
  read -p ""
  
  cleanup
fi

stop_all_services() {
  et-kill-all
}

exit_if_no_active_radio () {
  if [ ! -e ${ACTIVE_RADIO} ]; then
    echo "${ACTIVE_RADIO} file not found"    
    exit 1
  fi
}

get_mac_or_exit () {
  MAC=$(cat ${ACTIVE_RADIO} | jq -e -r .bluetooth.mac)
  if [ $? -ne 0 ]; then
    echo "No MAC address found in ${ACTIVE_RADIO}."
    exit 1
  fi
}

update_ax25_conf() {

  [ -z "$ET_USER_CONFIG" ] && ET_USER_CONFIG="${HOME}/.config/emcomm-tools/user.json"

  CALLSIGN=$(cat ${ET_USER_CONFIG} | jq -r .callsign)

  if [ "${CALLSIGN}" = "N0CALL" ]; then
    et-log "No callsign set. Run et-user first."
    exit 1
  fi

  GREP_OUT=$(grep ${AX25_PORT} ${AX25_CONF_FILE})
  if [ $? -eq 0 ]; then
    # Delete all existing AX.25 port  entries. Note: we can't use sed here
    # since we do not have permission to create a temporary file with
    # inline replacement. ed modifies the file directly.
    printf "g/^${AX25_PORT}/d\nw\nq\n" | ed ${AX25_CONF_FILE} > /dev/null 2>&1

  fi

  echo "${AX25_PORT} ${CALLSIGN} 1200 255	2 1200 Packet" >> ${AX25_CONF_FILE}
}

do_kiss_attach() {
  echo -e "${BLUE}Connecting KISS interface using ${BT_DEVICE} and AX.25 port: ${AX25_PORT}${NC}"
  KISS_OUT=$(sudo kissattach ${BT_DEVICE} ${AX25_PORT})
    
  if [[ $? -ne 0 ]]; then
    echo -e "${RED}Failed to attach KISS port.${NC}"
    exit 1
  fi

  KISS_OUT=$(sudo kissparms -c 1 -p ${AX25_PORT})
}

exit_on_service_failure() {
  local systemd_unit_name="$1"
  systemctl is-active --user --quiet "${systemd_unit_name}"
  if [ $? -ne 0 ]; then
    et-log "Can't start mode. ${systemd_unit_name} failed to start."
    exit 1
  fi
}

TIMEOUT=15  # Total time to wait in seconds
INTERVAL=2  # Interval between checks in seconds

start_and_wait_for_service() {
  local systemd_unit_name="$1"
  local elapsed=0

  et-log "Starting ${systemd_unit_name}..."
  systemctl --user start ${systemd_unit_name}

  et-log "Waiting for ${systemd_unit_name} to become active..."

  while ! systemctl is-active --user --quiet "${systemd_unit_name}"; do
      sleep "$INTERVAL"
      elapsed=$((elapsed + INTERVAL))
      if [ "$elapsed" -ge "$TIMEOUT" ]; then
          et-log "Timeout reached. ${systemd_unit_name} failed to start."
          exit 1
      fi
      et-log "Waiting for ${systemd_unit_name} to become active..."
  done

  # Some applications even when started still need a bit of extra time to be available. 
  # Add a short think time of 2 seconds. The ARDOP modem requies extra time.
  sleep 2

  # Do one more check to ensure that we are running, otherwise exit.
  exit_on_service_failure ${systemd_unit_name}
}


exit_if_no_active_radio

case $1 in
  p|pair)

    MAC=$(cat ${ACTIVE_RADIO} | jq -e -r .bluetooth.mac)
    if [ $? -eq 0 ]; then
      PAIR_STATUS=$(bluetoothctl info "${MAC}" | grep "Paired:" | awk '{print $2}')
      if [ "${PAIR_STATUS}" == "yes" ]; then
         echo "Device with MAC ${MAC} is already paired."
         exit 1
      fi
    fi 

    echo -e "${YELLOW}"
    echo -e "1. Turn off your ${DEVICE_NAME}"
    echo -e "2. Turn on your ${DEVICE_NAME}"
    echo -e "3. Goto Menu > Pairing and press OK"
    echo -e "4. Press ${WHITE}[ENTER]${YELLOW} when done.${NC}"
    read -p ""

    echo "Searching for ${DEVICE_NAME}..."

    HCI_OUT=$(hcitool scan | grep "${DEVICE_NAME}")
    if [ $? -ne 0 ]; then
      echo "Can't find ${DEVICE_NAME}. Make sure that the radio is on and in pairing mode."
      exit 1
    fi

	MAC=$(echo ${HCI_OUT} | awk '{print $1}')

    # Update configuration file detected MAC address
    sed -i "s|.*mac.*|    \"mac\": \"${MAC}\"|g" ${ACTIVE_RADIO}

    # Generate an expect script to automatically pair our device.
    PAIR_SCRIPT="${HOME}/pair"

cat <<EOF > ${PAIR_SCRIPT}
#!/usr/bin/expect -f

set device "${MAC}"
set timeout 60

spawn bluetoothctl
expect "Agent registered"
send "power on\r"
expect "Changing power on succeeded"
send "scan on\r"
expect "${MAC}"
send "pair ${MAC}\r"
expect "Pairing successful"
send "trust ${MAC}\r"
expect "trust succeeded"
send "exit\r"
expect eof
EOF

    sleep 1 && chmod 755 ${PAIR_SCRIPT} && expect ${PAIR_SCRIPT}

    ;;
  u|unpair)
    MAC=$(cat ${ACTIVE_RADIO} | jq -e -r .bluetooth.mac)
    if [ $? -ne 0 ]; then
      echo "No MAC found in radio configuration file."
      exit 1
    fi 

    PAIR_STATUS=$(bluetoothctl info "${MAC}")
    if [ $? -ne 0 ]; then
       echo "No device with ${MAC} found. Can't unpair."
       exit 1
    fi

    echo "Unpairing ${DEVICE_NAME}..."
    bluetoothctl untrust ${MAC}
    bluetoothctl remove ${MAC}

    echo -e "${YELLOW}"
    echo -e "You may also want to remove this device from your ${DEVICE_NAME}"
    echo -e "under Menu > General Settings > Connection > Paired Devices." 
    echo -e "${NC}"

    ;;
  c|connect)
    get_mac_or_exit

    echo -e "${YELLOW}"
    echo -e "1. Turn off your ${DEVICE_NAME}"
    echo -e "2. Turn on your ${DEVICE_NAME}"
    echo -e "3. Goto Menu > General Settings > KISS TNC > Enable KISS TNC"
    echo -e "4. Press ${WHITE}[ENTER]${YELLOW} when done.${NC}"
    read -p ""

    is_paired=$(bluetoothctl info ${MAC} | grep "Paired" | awk '{print $2}')
    if [ "${is_paired}" != "yes" ]; then
      echo -e "${RED}${DEVICE_NAME} is not paired. Run: '${WHITE}$(basename $0) pair${RED}'"
      echo -e "${NC}"
      exit 1
    fi  
 
    echo -e "Found ${DEVICE_NAME} with MAC address: ${MAC}" 

    update_ax25_conf 

    sudo rfcomm connect /dev/rfcomm0 ${MAC} 1>/dev/null 2>&1 &

    sleep 10 && reset

    if [[ ! -e "${BT_DEVICE}" ]]; then
      echo -e "${RED}Failed to connect via Bluetooth${NC}"
      echo -e "${YELLOW}"
      echo -e "1. Try turning off and back on your radio."
      echo -e "2. Check your radio and ensure that ${BLUE}$(hostname)${YELLOW} is listed under:"
      echo -e "   Menu > General Settings> Connection > Paired Devices: $(hostname)."
      echo -e ""
      echo -e "   If it is not listed, re-run: ${WHITE}$(basename $0) pair${NC}\n"
      exit 1
    fi

    stop_all_services

    echo -e "\nSelect a packet application to start:"
    options=("axcall" "QtTermTCP" "Winlink" "YAAC" "Exit")

    select opt in "${options[@]}"
    do
      case $opt in
        "axcall")
          do_kiss_attach
          echo -e "${YELLOW}"
          echo -e "SAMPLE COMMANDS               DESCRIPTION                     "
          echo -e "----------------------------- --------------------------------"
          echo -e "axcall wl2k <STATION>         Connect directly to a node      "
          echo -e "axcall wl2k <STATION> <DIGI>  Connect to node via a digipeater"
          echo -e "sudo axlisten -a -tttt -c     Monitor inbound and outbound traffic"
          echo -e "${NC}"

          # We can't have the rfcomm and kiss processes killed.
          CLEAN_UP=0
          break
          ;;
        "QtTermTCP")
          et-qttermtcp start 1>/dev/null 2>&1
          break
          ;;
        "Winlink")
          do_kiss_attach
          start_and_wait_for_service et-service-winlink-native-packet
          min http://localhost:8080 1>/dev/null 2>&1
          break
          ;;
        "YAAC")
          et-yaac start
          break
          ;;
        "Exit")
          break
          ;;
        *)
          echo "Invalid option, please select a number from the list."
          ;;
       esac
    done

    ;;
  *)
    et-log "Invalid command."
    usage
    exit 1
  ;;
esac
