#!/bin/bash

CMD=${0##*/}
DATE_FORMAT='[%Y-%m-%d %H:%M:%S]'
LOG_QUIET=0
LOG_ERROR=1
LOG_WARN=2
LOG_INFO=3
LOG_DEBUG=4

: ${LOGLEVEL:=$LOG_INFO}
test -t 1 || NO_VALUE=_
: ${NO_VALUE:=' '}

# ----------------------------------------------------------------------------
# Functions
# ----------------------------------------------------------------------------

mylog() {
    echo "$(date +"$DATE_FORMAT")$@"
}
mylogn() {
    echo -n "$(date +"$DATE_FORMAT")$@"
}

logdebug() {
    test $LOGLEVEL -ge $LOG_DEBUG || return 0
    if [[ $1 == '-n' ]]; then
        shift
        mylogn "[DEBUG] $@" >&2
    else
        mylog "[DEBUG] $@" >&2
    fi
}

loginfo() {
    test $LOGLEVEL -ge $LOG_INFO || return 0
    if [[ $1 == '-n' ]]; then
        shift
        mylogn "[INFO] $@"
    else
        mylog "[INFO] $@"
    fi
}

logwarn() {
    test $LOGLEVEL -ge $LOG_WARN || return 0
    if [[ $1 == '-n' ]]; then
        shift
        mylogn "[WARN] $@" >&2
    else
        mylog "[WARN] $@" >&2
    fi
}

logerror() {
    test $LOGLEVEL -ge $LOG_ERROR || return 0
    if [[ $1 == '-n' ]]; then
        shift
        mylogn "[ERROR] $@" >&2
    else
        mylog "[ERROR] $@" >&2
    fi
}

usage() {
  cat <<EOU
usage: $CMD

env:

  LOGLEVEL: $LOGLEVEL

examples:

  # List AP station status
  $0

  # List a specific station status
  $0 <station_mac>

EOU
}

STATION_MAC=$1

print_header() {
    HDR_LENGTH=0
    for stact in $STA_COLS
    do
        IFS=: read stac stacl stacu <<<$(echo $stact)
        test -n "$stacl" || stacl=-20
        printf "%${stacl}s " ${stac^^}
        let HDR_LENGTH+=${stacl#-}+1
    done
    echo
}

local_api() {
    curl -s "http://localhost:8841/v1/$1"
}

local_api_with_code_checked() {
    # Check if curl version supports --fail-with-body (introduced in 7.76.0)
    curl_version=$(curl --version | head -n1 | sed 's/curl \([0-9.]*\).*/\1/')
    if [[ "$(printf '%s\n' "7.76.0" "$curl_version" | sort -V | head -n1)" == "7.76.0" ]]; then
        curl -s --fail-with-body "http://localhost:8841/v1/$1"
    else
        # Fallback for older curl versions
        curl -s -f "http://localhost:8841/v1/$1" || curl -s "http://localhost:8841/v1/$1"
    fi
}

local_simple_post_api() {
    curl -XPOST -s "http://localhost:8841/v1/$1"
}

frcc() {
    local_api config/active
}

hl() {
    ${HEADER:-true} || return 0
    for ((i=0;i<HDR_LENGTH;i++)); do
        echo -n '-'
    done
    echo
}

timeit() {
    return 0
    tnow=$(date +%s%3N)
    echo "TIMEIT $1: $((tnow-tlast))" >&2
    tlast=$tnow
}

displaytime() {
    ${FORMAT_TIME:-true} || {
        echo "$1"
        return 0
    }
    local T=$1
    local D=$((T/60/60/24))
    local H=$((T/60/60%24))
    local M=$((T/60%60))
    local S=$((T%60))
    (( D > 0 )) && printf '%02dd' $D
    (( H > 0 )) && printf '%02dh' $H
    (( M > 0 )) && printf '%02dm' $M
    printf '%02ds\n' $S
}

displaynumber() {
  local number=$1
  local units=("B" "K" "M" "G")
  local unit_index=0

  echo "$number" | egrep -v '^[0-9]+$' && return 0
  while [[ $(echo "$number >= 1000" | bc) -eq 1 && $unit_index -lt $((${#units[@]} - 1)) ]]; do
    number=$(echo "scale=3; $number / 1000" | bc)
    ((unit_index++))
  done

  # Format the number to ensure the total output string is no more than 5 characters
  local formatted_number=$(printf "%.3g" "$number")
  formatted_number=${formatted_number:0:4}  # Truncate to 4 characters (including dot)

  printf "%s%s\n" "${formatted_number%.}" "${units[unit_index]}"
}

get_sta_name() {
  m=$1
  result=$(redis-cli --raw hget host:mac:$m name)
  test -n "$result" && {
    echo "$result"
    return 0
  }

  result=$(redis-cli --raw hget host:mac:$m detect|jq -r .name)
  test "$result" == null || {
    echo "$result"
    return 0
  }

  redis-cli --raw hget host:mac:$m bname
}

# ----------------------------------------------------------------------------
# MAIN goes here
# ----------------------------------------------------------------------------

STA_COLS='sta_mac sta_ip:-17 ap_uid:9 band:4 chan:5 mimo:5 rssi:5 snr:5 tx:5 rx:5 ctx:6 crx:6 intf:-8 mlo:7 vlan:5 dvlan:5 assoc_time:14 idle:6 hb_time:9 ssid:-15 wpax:-15 ap_name:-20:u sta_name:-30:u'
(print_header; hl) >&2
lines=0
timeit begin

test -z "$STATION_MAC" || local_simple_post_api "control/monitor/$STATION_MAC"

set -o pipefail

while true; do
    if [[ -z "$STATION_MAC" ]]; then
        sta_data=$(local_api_with_code_checked status/station/flat| jq -r '.info[] | with_entries(if .key == "wpax" then . else . end) + {"wpax": (.wpax // "-")} | [.macAddr, .assetUID, .ssid, .wpax, .band, .channel, .txnss, .rxnss, .rssi, .snr, .txRate, .rxRate, .curTxRate//"_", .curRxRate//"_", .intf, has("mlo"), .mlo.standby//"false", .assocTime, .ts, .idle, .dvlanVlanId, .vlanId]|@tsv' 2>/dev/null)  ||\
        sta_data=$(local_api_with_code_checked status/station| jq -r '.info|to_entries[]|.value + {"wpax": (.value.wpax // "-")} | [.key, .assetUID, .ssid, .wpax, .band, .channel, .txnss, .rxnss, .rssi, .snr, .txRate, .rxRate, .curTxRate//"_", .curRxRate//"_", .intf, has("mlo"), .mlo.standby//"false", .assocTime, .ts, .idle, .dvlanVlanId, .vlanId]|@tsv')
    else 
        sta_data=$(local_api_with_code_checked status/station/$STATION_MAC/flat| jq -r '.info[] | with_entries(if .key == "wpax" then . else . end) + {"wpax": (.wpax // "-")} | [.macAddr, .assetUID, .ssid, .wpax, .band, .channel, .txnss, .rxnss, .rssi, .snr, .txRate, .rxRate, .curTxRate//"_", .curRxRate//"_", .intf, has("mlo"), .mlo.standby//"false", .assocTime, .ts, .idle, .dvlanVlanId, .vlanId]|@tsv' 2>/dev/null) ||\
        sta_data=$(local_api_with_code_checked status/station/$STATION_MAC| jq -r '.info | with_entries(if .key == "wpax" then . else . end) + {"wpax": (.wpax // "-")} | [.macAddr, .assetUID, .ssid, .wpax, .band, .channel, .txnss, .rxnss, .rssi, .snr, .txRate, .rxRate, .curTxRate//"_", .curRxRate//"_", .intf, has("mlo"), .mlo.standby//"false", .assocTime, .ts, .idle, .dvlanVlanId, .vlanId]|@tsv' 2>/dev/null)
    fi
    timeit sta-data
    test -n "$sta_data" && echo "$sta_data" | while IFS=$'\t' read sta_mac ap_mac sta_ssid sta_wpax sta_band sta_channel sta_txnss sta_rxnss sta_rssi sta_snr sta_tx_rate sta_rx_rate sta_ctx_rate sta_crx_rate sta_intf sta_mlo sta_mlo_standby sta_assoc_time sta_ts sta_idle sta_dvlan sta_vlan
    do
        test -n "$sta_mac" || continue
        timeit read-$sta_mac
        sta_ip=$(redis-cli --raw hget host:mac:$sta_mac ipv4Addr)
        timeit sta_ip
        timeit read
        ap_name=$(redis-cli --raw hget host:mac:$ap_mac name || echo $NO_VALUE)
        timeit ap_name
        time_now=$(date +%s)
        timeit timestamp

        for stact in $STA_COLS
        do
            IFS=: read stac stacl stacu <<<$(echo $stact)
            timeit "process-col-$stac"
            test -n "$stacl" || stacl=-20
            case $stac in
                sta_mac) stad=$sta_mac ;;
                sta_ip) stad=$sta_ip ;;
                sta_name) stad=$(get_sta_name ${sta_mac^^}) ;;
                ap_uid) stad=${ap_mac:9} ;;
                ap_name) stad=$ap_name ;;
                ssid) stad=$sta_ssid ;;
                wpax) stad=$sta_wpax ;;
                band) stad=$sta_band ;;
                chan) stad=$sta_channel ;;
                mimo) stad="${sta_txnss}x${sta_rxnss}" ;;
                rssi) stad=$sta_rssi ;;
                snr) stad=$sta_snr ;;
                vlan) stad=$sta_vlan ;;
                dvlan) stad=$sta_dvlan ;;
                idle) stad=$sta_idle ;;
                tx) stad=$sta_tx_rate ;;
                rx) stad=$sta_rx_rate ;;
                ctx) stad=$(displaynumber $sta_ctx_rate) ;;
                crx) stad=$(displaynumber $sta_crx_rate) ;;
                intf) stad=$sta_intf ;;
                mlo) 
                    if $sta_mlo; then
                        if [[ $sta_mlo_standby == "true" ]]; then
                            stad=STANDBY
                        else
                            stad=ACTIVE
                        fi
                    else
                        stad=$NO_VALUE
                    fi
                    ;;
                assoc_time) stad=$(displaytime $sta_assoc_time) ;;
                hb_time) stad=$( displaytime $((time_now - sta_ts)) );;
                *) stad=$NO_VALUE ;;
            esac
            timeit "case-$stac"
            test -t 1 || stad=${stad// /_}
            stad=${stad//[‘’]/\'}
            stadl=${#stad}
            # process unicode string
            test "$stacu" == 'u' && {
                stadlu=$(perl -CSAD -E 'say length($ARGV[0])' -- "$stad")
                #stadlu=$(echo "$stad"|python3 -c 'v=input();print(len(v))')
                stadlL=$(echo "$stad" | wc -L)
                let stacld=stadl-stadlu*2+stadlL
                test $stadl -eq $stadlu || {
                    if [[ ${stacl:0:1} == '-' ]]; then
                        let stacl=stacl-stacld
                    else
                        let stacl=stacl+stacld
                    fi
                }
            }
            timeit 'stadcl-adjust'
            stacla=${stacl#-}
            if [[ $stadl -gt $stacla ]]
            then
                stad="${stad:0:$(((stacla-2)/2))}..${stad:$((stadl-(stacla-2)/2))}"
            fi
            printf "%${stacl}s " "${stad:-$NO_VALUE}"
            timeit 'printf'
        done
        let lines++
        echo
    done
    if [[ -z "$STATION_MAC" ]]; then
        break # no endlessly query when station mac is not specified
    fi
    sleep 2
done
timeit 'done'
tty_rows=$(stty size | awk '{print $1}')
(( lines > tty_rows-2 )) && {
    ( hl; print_header ) >&2
}
