#!/bin/bash
#
# Lazy boring
# Ubuntu-MM
# Idea from Xaomi
# Ko Ko Ye <kokoye2007@gmail.com>
# Ubuntu Myanmar LoCo Team
# https://github.com/kokoye2007/wifi-qr
#
# References:
#   [1]  section 7, https://www.wi-fi.org/system/files/WPA3 Specification v3.2.pdf
#   [2]  https://github.com/zxing/zxing/wiki/Barcode-Contents#wi-fi-network-config-android-ios-11

WIFIS=''
SSID=''
KEEY=''
PSK=''
QSSID=''
QKEES=''
QSSIDO=''
QHIDE=false
QTRANS=''
LEGACY_MODE=''
encode=percent_encode
W_TEXT_LINE="------------------------------------------------------"

# Define configuration paths following XDG Base Directory Specification
CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
CONFIG_DIR="${CONFIG_HOME}/wifi-qr"
CONFIG_FILE="${CONFIG_DIR}/config"

RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
BLUE=$(tput setaf 4)
RESET=$(tput sgr0)
BOLD=$(tput bold)
VERSION="0.4"

#RAW DATA SAMPLE
#"WIFI:S:$SSID;P:$PASSWORD;T:$KEY_TYPE;H:$true;"
#qrencode -o - -t UTF8
#qrencode -l h -s 14 -o

# Display info message
w_info()
{
    printf '%s\n%b\n%s\n' "${W_TEXT_LINE}" "${@}" "${W_TEXT_LINE}" >&2

    case ${WIFIQR_GUI} in
        zenity) ${WIFIQR_GUI} --info --width=400 --title="WiFi-QR" --text="${*//\\/\\\\}";;
        kdialog) ${WIFIQR_GUI} --title "WiFi-QR" --msgbox "$@";;
        none) ;;
    esac
}

# Display warning message to stderr
w_warn()
{
    printf '%s\nwarning: %b\n%s\n' "${W_TEXT_LINE}" "${*}" "${W_TEXT_LINE}" >&2

    case ${WIFIQR_GUI} in
        zenity) zenity --warning --width=400 --title="WiFi-QR" --text="${*//\\/\\\\}";;
        kdialog) kdialog --title "WiFi-QR" --sorry "$@";;
        none) ;;
    esac
}

# Display warning message with cancel option
w_warn_cancel()
{
    printf '%s\n%b\n%s\n' "${W_TEXT_LINE}" "${@}" "${W_TEXT_LINE}" >&2

    case ${WIFIQR_GUI} in
        zenity) zenity --warning --width=400 --title="WiFi-QR" --text="${*//\\/\\\\}";;
        kdialog) kdialog --title "WiFi-QR" --warningcontinuecancel "$@" ;;
        none) ;;
    esac
}

# Display fatal error message and terminate script
w_die()
{
    printf '%s\nerror: %b\n%s\n' "${W_TEXT_LINE}" "${@}" "${W_TEXT_LINE}" >&2

    case ${WIFIQR_GUI} in
        zenity) zenity --error --width=400 --title="WiFi-QR" --text="${*//\\/\\\\}";;
        kdialog) kdialog --title "WiFi-QR" --error "$@";;
        none) ;;
    esac

    exit 1
}

# GUI Main Menu
main_menu() {
    [ -z "$WIFIQR_GUI" ] && wifiqr_detect_gui

    case "${WIFIQR_GUI}" in
        zenity)
            CHOICE=$(zenity --list \
                --title="WiFi and QR Code" \
                --text="Choose:" \
                --width=320 --height=360 \
                --column="Menu:" \
                  "Scan and connect" \
                  "Scan Image and connect" \
                  "Share current WiFi" \
                  "Share saved WiFi" \
                  "Webcam Configure" \
                  "See license" \
                  "See version")
            ;;
        kdialog)
            CHOICE=$(kdialog --title "WiFi-QR" --menu "WiFi and QR Code" \
                1 "Scan and connect" \
                2 "Scan Image and connect" \
                3 "Share current WiFi" \
                4 "Share saved WiFi" \
                5 "Webcam Configure" \
                6 "See license" \
                7 "See version")
            # Convert kdialog numeric selection to text
            case "$CHOICE" in
                1) CHOICE="Scan and connect" ;;
                2) CHOICE="Scan Image and connect" ;;
                3) CHOICE="Share current WiFi" ;;
                4) CHOICE="Share saved WiFi" ;;
                5) CHOICE="Webcam Configure" ;;
                6) CHOICE="See license" ;;
                7) CHOICE="See version" ;;
                *) exit 0 ;;
            esac
            ;;
        *)
            w_die "Unsupported GUI: ${WIFIQR_GUI}"
            ;;
    esac

    # Menu Selection
    if [[ "$CHOICE" =~ "Share saved WiFi" ]]; then
        call_wifi_gui

    elif [[ "$CHOICE" =~ "Share current WiFi" ]]; then
        call_current_wifi_gui

    elif [[ "$CHOICE" =~ "Scan and connect" ]]; then
        call_wifi_scan_gui "cam"

    elif [[ "$CHOICE" =~ "Scan Image and connect" ]]; then
        call_file_select_and_scan_gui

    elif [[ "$CHOICE" =~ "Webcam Configure" ]]; then
        get_webcam_devices
        main_menu

    elif [[ "$CHOICE" =~ "See license" ]]; then
        case "${WIFIQR_GUI}" in
            zenity)
                zenity --text-info --title="WiFi-QR Copyright" --filename=/usr/share/doc/wifi-qr/copyright --width=528 --height=780
                ;;
            kdialog)
                kdialog --title "WiFi-QR Copyright" --textbox /usr/share/doc/wifi-qr/copyright 528 780
                ;;
        esac
        main_menu

    elif [[ "$CHOICE" =~ "See version" ]]; then
        case "${WIFIQR_GUI}" in
            zenity)
                zenity --info --icon=wifi-qr --title="WiFi-QR Version" --no-wrap --text=" This is wifi-qr version $VERSION"
                ;;
            kdialog)
                kdialog --title "WiFi-QR Version" --msgbox "This is wifi-qr version $VERSION"
                ;;
        esac
        main_menu

    else
        exit 0
    fi
}

wifilist_call(){
  mapfile -t wifilist < <(nmcli  -g NAME,TYPE connection | grep 802-11-wireless | sed 's/:802-11-wireless//' | sed 's/\\\([\\;,:"\]\)/\1/g' )
}

# GUI
call_wifi_gui() {
    wifilist_call

    [ -z "$WIFIQR_GUI" ] && wifiqr_detect_gui

    case "${WIFIQR_GUI}" in
        zenity)
          WIFIS=$(zenity --list --column="SSID" \
    	          --width=300 --height=600 \
    	          --title="Select SSID" \
    	          "${wifilist[@]}")
          ;;
        kdialog)
          WIFIS=$(kdialog --combobox "Select SSID" \
                "${wifilist[@]}")
          ;;
        *)
            w_die "Unsupported GUI: ${WIFIQR_GUI}"
          ;;
    esac
    if [ -z "$WIFIS" ]; then
      exit 0
    else
      call_qr_gui
    fi
}

# Call QR GUI
call_qr_gui() {
  call_wifi_pass
  QR_FILE_NAME="wifi-qr-$(echo "$WIFIS" | tr -dc '[:alnum:]').png"
  SAVE_QR_DIR="${HOME}/Pictures/"

  [ -z "$WIFIQR_GUI" ] && wifiqr_detect_gui

  case "${WIFIQR_GUI}" in
      zenity)
        QR_PNG=$(zenity --title="WiFi-QR" --file-selection --save --file-filter="PNG files | *.png" --filename="$SAVE_QR_DIR"/"$QR_FILE_NAME")
        ;;
      kdialog)
        QR_PNG=$(kdialog --title "WiFi-QR" --getsavefilename "$SAVE_QR_DIR"/"$QR_FILE_NAME" "*.png")
        ;;
  esac
  if [[ -z "$QR_PNG" ]]; then
	  main_menu
  else
    qrencode -l h -s 14 -o "$QR_PNG" "WIFI:S:$SSID;P:$KEEY;${PSSK:+$PSSK;}${H:+$H;};"
    xdg-open "$QR_PNG"
  fi
}

call_current_wifi_gui() {
  current_wifi_ssid
  call_qr_gui
}

#WiFi Command from terminal
call_wifi_terminal() {
  wifilist_call
  OLD_IFS=$IFS
  IFS=$'\n'
  for l in "${wifilist[@]}"; do
    array[i]=$l
    i=$((i + 1))
  done
  IFS=$OLD_IFS

  i=0
  for m in "${array[@]}"; do
    echo
    echo "$((i++))) $(basename "$m")"
  done
  IFS=$OLD_IFS
  echo ""
  echo -e "${BLUE}===============================================================${RESET}"
  echo -e "${BOLD}${WHITE}Please choose WiFi ssid ${RESET}"
  echo -e "${BLUE}===============================================================${RESET}"
  read -e -r wifissid
  WIFIS="${array[$wifissid]}"
  echo -e "${BLUE}===============================================================${RESET}"
  echo -e "\nYour chosen SSID File is: $WIFIS\n ${RESET}"
  echo -e "${BLUE}===============================================================${RESET}"
  terminal_qr
}

#WiFi Command from terminal with FZF
call_wifi_terminal_fzf() {
     if command -v fzf &>/dev/null; then
  	    wifilist_call
  	    OLD_IFS=$IFS
  	    IFS=$'\n'
  	    for l in "${wifilist[@]}"; do
  	      array[i]=$l
  	      i=$((i + 1))
  	    done
  	    IFS=$OLD_IFS

  	    wifissid=$(printf '%s\n' "${array[@]}" | fzf)

  	    WIFIS="$wifissid"

  	    echo -e "${BLUE}===============================================================${RESET}"
  	    echo -e "\nYour chosen SSID File is: $WIFIS\n ${RESET}"
  	    echo -e "${BLUE}===============================================================${RESET}"
  	    terminal_qr
    else
        w_die "fzf not found. please install fzf."
    fi
}


terminal_qr() {
  call_wifi_pass
  qrencode -o - -t UTF8 "WIFI:S:$SSID;P:$KEEY;${PSSK:+$PSSK;}${H:+$H;};"
  echo
}

current_wifi_ssid() {
  WIFIS="$(nmcli -t -f active,ssid dev wifi | grep -E '^yes' | cut -d':' -f2- | tr -d '\r\n')"
  echo -e "${BLUE}===============================================================${RESET}"
  echo -e "\nYour current active SSID is:${BOLD}${RED} $WIFIS\n ${RESET}"
  echo -e "${BLUE}===============================================================${RESET}"
}

percent_encode() {
  local LC_ALL=C c
  declare -i len=${#1}
  for ((i=0; i < len; i++ )); do
    c="${1:i:1}"
    if [[ $c = [$'\x20'-$'\x3A'$'\x3C'-$'\x7E'] ]]; then
      printf %s "$c"
    else
      printf '%%%02X' "'$c"
    fi
  done
}

legacy_encode(){
  sed -E 's/[\;,":]/\\&/g' <<<"$1"
}

#Data Extractor nmcli version
call_wifi_pass() {
  WIFIS="${WIFIS//\\([;,:\"])/}"
  SSID=$(nmcli -g 802-11-wireless.ssid connection show "${WIFIS}")
  KEEY=$(nmcli -g 802-11-wireless-security.psk  -s connection show "${WIFIS}")
  PSK=$(nmcli -g 802-11-wireless-security.key-mgmt  connection show "${WIFIS}" )
  HIDN=$(nmcli -g 802-11-wireless.hidden connection show "${WIFIS}")

  if [[ "$PSK" == "sae" ]]; then
    PSSK="T:WPA;R:1"
  elif [[ "$PSK" == *"wpa"* ]]; then
    PSSK="T:WPA"
  elif [[ "$PSK" == "wep" ]]; then
    PSSK="T:WEP"
  else
    PSSK=""
  fi
  if [[ "$HIDN" == *"yes"* ]]; then
    H="H:true"
  fi
  echo -e "${GREEN}"
  echo ""
  echo "SSID:    $SSID"
  echo "PASS:    $KEEY"
  echo "TYPE:    $PSSK"
  echo "HIDE:    $H"
  echo ""
  echo -e "${RESET}"
  SSID=$($encode "$SSID")
  KEEY=$($encode "$KEEY")
}

call_zbar_cam_scan() {
  zbarwebcam=/dev/video0
  check_existing_config

  qr_data=$1
  zbarcam --raw --prescale=320x240 "$zbarwebcam" >"$qr_data" &

  # Last job running in background eg. zbarcam
  pid=$!
  # Sleep loop until $qr_data file has content
  local i=0
  local chars="/-\|"
  echo -en "  Scanning QR Code" "\r"
  while [[ ! -s $qr_data ]]; do
    sleep 0.5
    pgrep -x zbarcam >/dev/null && echo -en "${chars:$i:1}" "\r" || exit 0
    i=$(((i+1)%${#chars}))
    # cleanup - add a trap that will remove $qr_data and kill zbarcam
    # if any of the signals - SIGHUP SIGINT SIGTERM it received.
    trap 'rm -f "$qr_data" ; kill -s 9 "$pid"; exit' SIGHUP SIGINT SIGTERM
  done

  kill -s 9 $pid
  # to suppress the kill message
  # https://stackoverflow.com/a/5722874/11910267
  wait $pid 2>/dev/null
}

call_zbar_img_scan() {
  qr_data=$1
  img_path=$2
  zbarimg --raw "$img_path" >"$qr_data" 2>/dev/null
}

urimap_str(){
  local string=$1

  # Percent encoding is specified by the WiFi Alliance [1], but backslash
  # escapes might occur in legacy environments.  Legacy encoding is
  # documented in [2] but we cannot unambiguously distinguish between
  # them so we depend on LEGACY_MODE global parameter.

  if [[ $LEGACY_MODE ]]; then
    # Replace backslash escaped ";" with \xHH form so it doesn't interfere with \xXX splitting
    string=$(sed -E 's/(([^\]|^)(\\[^;])*)(\\;)/\1\\x3B/g' <<<"$string")
  fi

  local value
  local IFS=";"

  # Use an array to avoid SC2086 issue
  read -ra elements <<< "$string"

  IFS=$' \t\n'

  for element in "${elements[@]}"; do
    value=${element#*:}
    if [[ $LEGACY_MODE ]]; then
      value=$(sed -E -e ':L;s/\\([\;,":])/\1\n/;tL;s/\n//g' <<<"${value@E}")
    else
      value=${value//%/\\x}
      value=${value@E}
    fi

    # Quote variables as recommended by shellcheck
    echo "[${element%%:*}]=${value@Q}"

  done
}

call_wifi_scan() {
  mode=$1
  wifiqrdata=''
  # tmp data-matrix holder
  cwd="/tmp"
  qr_data="$cwd/wifi-qr-scan"

  if [[ $mode == "cam" ]]; then
    call_zbar_cam_scan "$qr_data"
  else
    call_zbar_img_scan "$qr_data" "$mode" # mode stores the image path
  fi

  wifiqrdata=$(< "$qr_data")
  rm -f "$qr_data"

  if [[ $wifiqrdata != WIFI:*\; ]]; then
    echo -e "${RED}============================${RESET}"
    echo -e "${BOLD}${WHITE}It is not a WiFi QR code ${RESET}"
    echo -e "${RED}============================${RESET}"
    echo -e "${BLUE}============================${RESET}"
    echo -e "${BOLD}${BLUE} $wifiqrdata ${RESET}"
    echo -e "${BLUE}============================${RESET}"
    QSSIDO=NOWIFI
    return
  else
    declare -A urimap

    # Capture the output of urimap_str in a variable first
    local urimap_result
    urimap_result=$(urimap_str "${wifiqrdata:5:-1}")

    # Then use eval with the variable, avoiding SC2046 warning
    eval urimap="(${urimap_result})"

    # eval urimap=\($(urimap_str "${wifiqrdata:5:-1}")\)

    local k v
    for k in "${!urimap[@]}"; do
      v=${urimap[$k]}
      case $k in
	      H)
	        QHIDE=$v
	        ;;
	      P)
	        QKEES=$v
	        ;;
	      S)
	        QSSID=$v
	        ;;
	      T)
	        QTYPE=$v
	        ;;
	      R)
	        QTRANS=$v
	        ;;
      esac
    done

    # Print the SSID, security type, password, and hidden attribute
    echo -e "${GREEN}============================${RESET}"
    echo -e "${BOLD}${BLUE} SSID: ${RESET} ${WHITE} $QSSID ${RESET}"
    echo -e "${BOLD}${BLUE} TYPE: ${RESET} ${WHITE} $QTYPE ${RESET}"
    echo -e "${BOLD}${BLUE} TRANSITION: ${RESET} ${WHITE} $QTRANS ${RESET}"
    echo -e "${BOLD}${BLUE} PASS: ${RESET} ${WHITE} $QKEES ${RESET}"
    echo -e "${BOLD}${BLUE} HIDE: ${RESET} ${WHITE} $QHIDE ${RESET}"
    echo -e "${GREEN}============================${RESET}"


    if [[ "$QHIDE" == "true" ]]; then
      echo -e "${GREEN}============================${RESET}"
      echo -e "${BOLD}${WHITE}It is HIDDEN ${RESET}"
      echo -e "${GREEN}============================${RESET}"

    elif nmcli device wifi list | grep -w "$QSSID"; then
      echo -e "${GREEN}============================${RESET}"
      echo -e "${BOLD}${WHITE}$QSSID is Online ${RESET}"
      echo -e "${GREEN}============================${RESET}"
      QSSIDO=ON

    else
      echo -e "${RED}============================${RESET}"
      echo -e "${BOLD}${WHITE}Cannot find $QSSID ${RESET}"
      echo -e "${RED}============================${RESET}"
      QSSIDO=OFF
    fi
  fi

  # key-mgmt type conversion
  if [[ -z "$QTYPE" ]] ; then
    NTYPE="none"
  elif [[ "$QTYPE" == "WEP" ]] ; then
    NTYPE="ieee8021x"
  elif [[ "$QTYPE" == "WPA" ]] ; then
    if [[ "$QTRANS" == 1 ]]; then
      # WPA3 Personal only
      NTYPE="sae"
    else
      # WPA3 Personal transition/WPA2/WPA
      NTYPE="wpa-psk"
    fi
  elif [[ "$QTYPE" == "SAE" ]]; then
    # This isn't standard, but at least google pixel phones use it.
    NTYPE="sae"
  else
    echo "unknown or need to add key-mgmt type"
    echo "$wifiqrdata"
  fi
}


call_wifi_scan_terminal() {
  call_wifi_scan "$1"
     if [[ "$QSSIDO" == "NOWIFI" ]]; then
	exit 0
     elif [[ "$QHIDE" == "true" ]]; then
	scan_connect_hidden
     elif [[ "$QSSIDO" == "ON" ]]; then
	scan_connect
     else
	echo "Cannot find Network and cannot Connect"
	exit 0
     fi
}

# Sample for SSID selection
call_wifi_scan_gui() {
  [ -z "$WIFIQR_GUI" ] && wifiqr_detect_gui

  call_wifi_scan "$1"

  # Function with QR data
  if [[ "$QSSIDO" =~ "ON" ]]; then
    case "${WIFIQR_GUI}" in
      zenity)
        zenity --question --title="Connect to WiFi" --text="Connect to '$QSSID'?" --width=200 --height=120 --icon=network-wireless 2>/dev/null
        ;;
      kdialog)
        kdialog --yesno "Connect to '$QSSID'?" --title="Connect to WiFi" --icon=network-wireless
        ;;
      *)
        w_die "Unsupported GUI: ${WIFIQR_GUI}"
        ;;
    esac
    connectn=$?

    if [ $connectn == 0 ]; then 
      scan_connect
    else
      exit
    fi

  elif [[ "$QSSIDO" =~ "OFF" ]]; then
    case "${WIFIQR_GUI}" in
      zenity)
        CHOICE=$(zenity --list \
          --title="Cannot find WiFi" \
          --text="Choose:" \
          --width=400 --height=180 \
          --column="Check" --column="Option" \
          --icon=network-wireless-offline \
          "Retry scanning internally" \
          "Retry submitting another QR Code")
        ;;
      kdialog)
        CHOICE=$(kdialog --title "WiFi-QR" \
          --menu "Cannot find WiFi" \
          1 "Retry scanning internally" \
          2 "Retry submitting another QR Code")
        # Translate numeric selection back to text
        case "$CHOICE" in
          1) CHOICE="Retry scanning internally" ;;
          2) CHOICE="Retry submitting another QR Code" ;;
          *) exit ;;
        esac
        ;;
      *)
        w_die "Unsupported GUI: ${WIFIQR_GUI}"
        ;;
    esac

    if [[ "$CHOICE" =~ "Retry scanning internally" ]]; then
      echo -e "${BOLD}Scanning in progress ... ${RESET}"
      nmcli radio wifi off
      sleep 5
      scan_connect
      if [[ $connectn != 0 ]]; then
        echo -e "${RED}============================${RESET}"
        echo -e "${BOLD}${WHITE}Cannot find $QSSID ${RESET}"
        echo -e "${RED}============================${RESET}"
        exit
      fi
    elif [[ "$CHOICE" =~ "Retry submitting another QR Code" ]]; then
      if [[ $1 == "cam" ]]; then
        call_wifi_gui "cam"
      else
        call_file_select_and_scan_gui
      fi
    else
      exit
    fi

  elif [[ "$QHIDE" =~ "true" ]]; then
    case "${WIFIQR_GUI}" in
      zenity)
        zenity --question --title="Connect to Hidden WiFi" --text="Connect to '$QSSID'?" --width=200 --height=120 --icon=network-wireless 2>/dev/null
        ;;
      kdialog)
        kdialog --yesno "Connect to '$QSSID'?" --title="Connect to Hidden WiFi" --icon=network-wireless
        ;;
      *)
        w_die "Unsupported GUI: ${WIFIQR_GUI}"
        ;;
    esac
    connectn=$?

    if [ "$connectn" = 0 ]; then
      scan_connect_hidden
    else
      exit
    fi
  fi
}

# SSID selection function
select_ssid() {
  [ -z "$WIFIQR_GUI" ] && wifiqr_detect_gui

  case "${WIFIQR_GUI}" in
    zenity)
      WIFIS=$(zenity --list --column="SSID" \
        --width=300 --height=600 \
        --title="Select SSID" \
        "${wifilist[@]}")
      ;;
    kdialog)
      # For kdialog, convert array to menu items
      local kdialog_args=()
      for ssid in "${wifilist[@]}"; do
        kdialog_args+=("$ssid" "$ssid")
      done
      WIFIS=$(kdialog --title "WiFi-QR" --menu "Select SSID" "${kdialog_args[@]}")
      ;;
    *)
      w_die "Unsupported GUI: ${WIFIQR_GUI}"
      ;;
  esac
}

call_file_select_and_scan_gui () {
  [ -z "$WIFIQR_GUI" ] && wifiqr_detect_gui

  case "${WIFIQR_GUI}" in
    zenity)
      img_path=$(zenity --file-selection --title="WiFi-QR" --file-filter="Image Files | *.png *.jpg *.jpeg *.gif")
      ;;
    kdialog)
      img_path=$(kdialog --title "WiFi-QR" --getopenfilename "$HOME" "Image Files (*.png *.jpg *.jpeg *.gif)")
      ;;
    *)
      w_die "Unsupported GUI: ${WIFIQR_GUI}"
      ;;
  esac

  if [ -z "$img_path" ]; then
    case "${WIFIQR_GUI}" in
      zenity)
        zenity --error --text="No file selected"
        ;;
      kdialog)
        kdialog --title "WiFi-QR" --error "No file selected"
        ;;
    esac
    exit
  fi

  call_wifi_scan_gui "$img_path"
}

scan_connect() {
  nmcli radio wifi on
  nmcli dev wifi rescan >/dev/null 2>&1
  nmcli dev wifi connect "$QSSID" password "$QKEES" >/dev/null 2>&1
}

scan_connect_hidden() {
  nmcli radio wifi on
  nmcli con delete "$QSSID" >/dev/null 2>&1
  nmcli con add type wifi con-name "$QSSID" ssid "$QSSID" >/dev/null 2>&1
  nmcli con modify "$QSSID" wifi-sec.key-mgmt "$NTYPE" >/dev/null 2>&1
  nmcli con modify "$QSSID" wifi-sec.psk "$QKEES" >/dev/null 2>&1
  nmcli con modify "$QSSID" wifi.hidden yes  >/dev/null 2>&1
  nmcli con up "$QSSID" >/dev/null 2>&1
}

usage() {
  echo ""
  echo " Usage: $(basename "$0") [-g] [-c] [-d zenity|kdialog] [-t] [-s] [-z] [-f file] [-p] [-q] [-L] [-v] [-w] [-h]"
  echo ""
  echo " -g	 Launch GUI Main Menu"
  echo " -d	 Specify GUI tool (zenity or kdialog)"
  echo " -c	 Launch WiFi QR Create GUI"
  echo " -t	 Launch WiFi QR Create Terminal"
  echo " -z	 Launch WiFi QR Create Terminal Fuzzy Finder"
  echo " -s	 Launch QR Scan and Auto Connect WiFi"
  echo " -f file Terminal [file] QR Scan and Auto Connect WiFi from file"
  echo " -p 	 Launch GUI [file] QR Scan and Auto Connect WiFi from file"
  echo " -q	 Launch QR Scan and Connect WiFi GUI"
  echo " -L      Use legacy (backlash) encoding/decoding"
  echo " -v	 Show WiFi-QR Version $VERSION"
  echo " -w	 Launch WiFi QR Webcam Selector"
  echo " -h	 Show this help message"
  echo ""
  show_current_password
}

show_current_password(){
  nmcli device wifi show-password
}

check_file() {
  if [ ! -f "$1" ]; then
    w_die "Error: $1 is not a file."
  fi

  file_type=$(file -b --mime-type "$1")
  if [ "$file_type" != "image/png" ] && [ "$file_type" != "image/jpeg" ]; then
    w_die "Error: $1 is not an image file."
  fi
}

# Check if device in config exists and is available
check_existing_config() {
    if [ -f "${CONFIG_FILE}" ]; then
        local current_device
        local current_device_name

        if ! current_device=$(grep "qrwebcam=" "${CONFIG_FILE}" | cut -d= -f2); then
            w_warning "Failed to read webcam device from config."
            get_webcam_devices
            return
        fi

        if ! current_device_name=$(grep "qrcamname=" "${CONFIG_FILE}" | cut -d= -f2); then
            w_warning "Failed to read webcam name from config."
            get_webcam_devices
            return
        fi

        if [ -n "$current_device" ]; then
            if [ -e "$current_device" ] && [ -r "$current_device" ]; then
                w_info "Configured webcam device is available: $current_device"
                zbarwebcam=$current_device
                return 0
            fi

            [ -z "$WIFIQR_GUI" ] && wifiqr_detect_gui

            # Device not available - ask user what to do
            if command -v "${WIFIQR_GUI}" &>/dev/null; then
                local action

                case "${WIFIQR_GUI}" in
                    zenity)
                        if ! action=$(zenity --list \
                            --title="Webcam device not available" \
                            --text="The configured webcam '$current_device_name' is not available." \
                            --column="Action" \
                            "Select new webcam device" \
                            "Remove configuration" \
                            --width=400 \
                            --height=280);
                        then
                            w_die "Dialog cancelled."
                        fi
                        ;;
                    kdialog)
                        if ! action=$(kdialog --title "Webcam device not available" --combobox "The configured webcam '$current_device_name' is not available.\nSelect an action:" \
                            "Select new webcam device" "Remove configuration");
                        then
                            w_die "Dialog cancelled."
                        fi
                        ;;
                    *)
                        w_die "Unsupported GUI: ${WIFIQR_GUI}"
                        ;;
                esac

                case "$action" in
                    "Select new webcam device")
                        get_webcam_devices
                        ;;
                    "Remove configuration")
                        rm -f "${CONFIG_FILE}"
                        w_info "Configuration removed."
                        ;;
                    *)
                        w_die "No valid action selected."
                        ;;
                esac
            else
                # Fallback to command line interface
                w_warning "The configured webcam device '$current_device_name' is not available."
                echo "Options:"
                echo "1) Select new webcam device"
                echo "2) Remove configuration"
                echo "3) Exit"
                echo -n "Select option (1-3): "
                read -r choice

                case "$choice" in
                    1)
                        get_webcam_devices
                        ;;
                    2)
                        rm -f "${CONFIG_FILE}"
                        w_info "Configuration removed."
                        ;;
                    *)
                        w_die "No valid action selected."
                        ;;
                esac
            fi
        else
            w_warning "Invalid configuration format."
            get_webcam_devices
        fi
    else
        w_info "No existing configuration found."
        unset current_device
    fi
}


# Get webcam devices using sysfs
get_webcam_devices() {
    local device_list=()
    local seen_names=()

    [ -z "$WIFIQR_GUI" ] && wifiqr_detect_gui

    # Helper: Check if a device name is already in the seen list
    is_device_duplicate() {
        local name="$1"
        for seen in "${seen_names[@]}"; do
            [ "$seen" = "$name" ] && return 0
        done
        return 1
    }

    # Scan video devices in sysfs (even numbers only)
    for sysdev in /sys/class/video4linux/video[02468]; do
        if [ -d "$sysdev" ] && [ -f "$sysdev/name" ]; then
            local id dev name

            id=$(basename "$sysdev") || { w_warning "Failed to get device ID for $sysdev."; continue; }
            dev="/dev/$id"

            # Check if the device is valid
            if [ -c "$dev" ] && [ -r "$dev" ]; then
                name=$(<"$sysdev/name") || { w_warning "Failed to read name for $sysdev."; continue; }

                # Skip duplicate device names
                if ! is_device_duplicate "$name"; then
                    seen_names+=("$name")
                    device_list+=("$id" "$name" "$dev")
                fi
            fi
        fi
    done

    # Handle no devices found
    if [ ${#device_list[@]} -eq 0 ]; then
        if command -v "${WIFIQR_GUI}" &>/dev/null; then
            case "${WIFIQR_GUI}" in
                zenity)
                    zenity --error --title="No Webcam Found" --text="No webcam devices were detected on the system."
                    ;;
                kdialog)
                    kdialog --title "No Webcam Found" --error "No webcam devices were detected on the system."
                    ;;
                *)
                    w_die "Unsupported GUI: ${WIFIQR_GUI}"
                    ;;
            esac
        else
            w_die "No webcam devices detected."
        fi
        exit 1
    fi

    present_dialog() {
        local selected
        case "${WIFIQR_GUI}" in
            zenity)
                selected=$(zenity --list \
                    --title="Select Webcam Device" \
                    --column="ID" --column="Name" --column="Device" \
                    "${device_list[@]}" \
                    --print-column=2,3 \
                    --width=500 --height=300) || return 1
                ;;
            kdialog)
                # Build the menu for kdialog
                local menu=()
                for ((i = 0; i < ${#device_list[@]}; i += 3)); do
                    menu+=("${device_list[i]}" "\"${device_list[i + 1]}\"")
                done
    
                selected=$(kdialog --title "WiFi-QR" --menu "Select Webcam Device:" "${menu[@]}" --geometry 500x300) || return 1
    
                # Extract the device name and path based on the selected ID
                local id="$selected"
                local index=-1
                for ((i = 0; i < ${#device_list[@]}; i += 3)); do
                    if [ "${device_list[i]}" = "$id" ]; then
                        index=$i
                        break
                    fi
                done
    
                if [ $index -ne -1 ]; then
                    selected="${device_list[index + 1]}|${device_list[index + 2]}"
                else
                    w_die "Failed to match selected device ID."
                fi
                ;;
            *)
                w_die "Unsupported GUI: ${WIFIQR_GUI}"
                ;;
        esac
        echo "$selected"
    }
    
    # Present GUI dialog if available
    if command -v "${WIFIQR_GUI}" &>/dev/null; then
        local selected device name
        if selected=$(present_dialog); then
            name=$(echo "$selected" | cut -d'|' -f1)
            device=$(echo "$selected" | cut -d'|' -f2)
            save_config "$name" "$device"
        else
            w_die "No device selected."
        fi
    else
        # CLI fallback
        w_info "Available webcam devices:"
        for ((i = 0; i < ${#device_list[@]}; i += 3)); do
            w_info "$((i / 3 + 1))) ${device_list[i + 1]} (${device_list[i + 2]})"
        done

        echo -n "Select device number (1-$(( ${#device_list[@]} / 3 ))): "
        read -r selection

        if [[ "$selection" =~ ^[0-9]+$ ]] && [ "$selection" -ge 1 ] && [ "$selection" -le $(( ${#device_list[@]} / 3 )) ]; then
            local idx=$(( (selection - 1) * 3 ))
            save_config "${device_list[idx + 1]}" "${device_list[idx + 2]}"
        else
            w_die "Invalid selection."
        fi
    fi
}

# Save configuration
save_config() {
    local name="$1"
    local device="$2"
    
    [ -z "$WIFIQR_GUI" ] && wifiqr_detect_gui

    if ! mkdir -p "${CONFIG_DIR}"; then
        w_die "Failed to create configuration directory: ${CONFIG_DIR}."
    fi
    
    if ! {
        echo "qrwebcam=${device}"
        echo "qrcamname=${name}"
    } > "${CONFIG_FILE}"; then
        w_die "Failed to write configuration to ${CONFIG_FILE}."
    fi
    
    if ! chmod 600 "${CONFIG_FILE}"; then
        w_die "Failed to set permissions for ${CONFIG_FILE}."
    fi
    
    if command -v "${WIFIQR_GUI}" &> /dev/null; then
        case "${WIFIQR_GUI}" in
            zenity)
                zenity --info \
                    --title="Webcam Selected" \
                    --text="Selected Webcam: ${name} (${device})"
                ;;
            kdialog)
                kdialog --title "Webcam Selected" --msgbox "Selected Webcam: ${name} (${device})"
                ;;
            *)
                w_warning "Unsupported GUI for confirmation: ${WIFIQR_GUI}."
                ;;
        esac
    else
        w_info "Selected Webcam: ${name} (${device})."
    fi
}

wifiqr_detect_gui()
{
    # If WIFIQR_GUI is already set, skip auto-detection
    [ -n "$WIFIQR_GUI" ] && return

    # Auto-detect based on desktop environment and available tools
    if [ "${XDG_CURRENT_DESKTOP}" = "KDE" ] && command -v kdialog &> /dev/null; then
        WIFIQR_GUI=kdialog
        WIFIQR_GUI_VERSION="$(kdialog --version)"
    elif command -v zenity &> /dev/null; then
        WIFIQR_GUI=zenity
        WIFIQR_GUI_VERSION="$(zenity --version)"
    elif command -v kdialog &> /dev/null; then
        WIFIQR_GUI=kdialog
        WIFIQR_GUI_VERSION="$(kdialog --version)"
    else
        w_die "No supported GUI (zenity or kdialog) found."
    fi

    # Print GUI version info
    echo "WiFi-QR GUI available, using ${WIFIQR_GUI} ${WIFIQR_GUI_VERSION##kdialog }"

    # Export for use in other scripts/functions
    export WIFIQR_GUI
}

if [[ $# -eq 0 ]] || [[ $1 != -* ]]; then
  usage
  exit 0
fi

while getopts ":gd:ctszf:pqLvwh" opt; do
    case $opt in
        g)
            action=main_menu
            ;;
        d)
            echo "Launching WiFi QR with $OPTARG..."
            WIFIQR_GUI="$OPTARG"
            ;;
        c)
            echo "Launching WiFi QR Create GUI..."
            action=call_wifi_gui
            ;;
        t)
            echo "Launching WiFi QR Create Terminal..."
            action=call_wifi_terminal
            ;;
        s)
            echo "Launching QR Scan and Auto Connect WiFi..."
            action="call_wifi_scan_terminal cam"
            ;;
        z)
            echo "Launching WiFi QR Create Terminal Fuzzy Finder..."
            action=call_wifi_terminal_fzf
            ;;
        f)
            check_file "$OPTARG"
            echo "Launching Terminal [file] QR Scan and Auto Connect WiFi from file: $OPTARG"
            action="call_wifi_scan_terminal ${OPTARG@Q}"
            ;;
        p)
            echo "Launching GUI [file] QR Scan and Auto Connect WiFi from file"
            action=call_file_select_and_scan_gui
            ;;
        q)
            echo "Launching QR Scan and Connect WiFi GUI..."
            action="call_wifi_scan_gui cam"
            ;;
        L)
            LEGACY_MODE=t
            encode=legacy_encode
            ;;
        v)
            echo -e "${GREEN}============================${RESET}"
            echo -e "${BOLD}${WHITE}  WiFi-QR $VERSION ${RESET}"
            echo -e "${GREEN}============================${RESET}"
            ;;
        w)
            echo "Select a webcam for the QR code scanner"
            action=get_webcam_devices
            ;;
        h)
            usage
            exit 0
            ;;
        \?)
            # echo "Invalid option: -$OPTARG" >&2
            w_die "Invalid option: -$OPTARG" >&2
            ;;
        :)
            w_die "Option -$OPTARG requires an argument."
            ;;
        *)
            usage
            exit 0
    esac
done
eval "$action"
