#!/bin/sh
# SPDX-License-Identifier: MIT
# shellcheck disable=SC2039
LANG=C

PING_COUNT=2
FORCE_RECONNECT_PING_NG_COUNT=2
FORCE_REBOOT_PING_NG_COUNT=$((FORCE_RECONNECT_PING_NG_COUNT+2))
CONFIG_FILE_EXT=_connection-recover.conf
NEW_CONFIG_FILE=/etc/atmark/connection-recover.conf
OLD_CONFIG_FILE_DIR=/etc/atmark/connection-recover/
NMCLI_UP_TIMEOUT_SEC=150
RESTART_SIM_NOT_FOUND_COUNT=2
FORCE_RECONNECT_DEVICE_UNMANAGED_COUNT=2
FORCE_RECONNECT_DEVICE_CONNECTING_COUNT=3

. /usr/sbin/connection-recover-log

# using put_log() at /usr/sbin/connection-recover-log
put_cr_trace_log() {
	put_trace_log connection-recover "$1"
}

put_cr_log() {
	put_log connection-recover "$1"
}

active_connection_exists() {
	if nmcli -f NAME connection | sed -e 's/ *$//' | grep -x -q "${CONNECTION}"; then
		return 0
	fi
	return 1
}

reconnect() {
	put_cr_log "$DEVICE reconnect start... "
	nmcli connection down "${CONNECTION}" > /dev/null 2>&1
	sleep 5
	timeout -sKILL ${NMCLI_UP_TIMEOUT_SEC} nmcli connection up "${CONNECTION}" > /dev/null 2>&1
	result=$?
	if [ ${result} -eq 0 ]; then
		put_cr_log "$DEVICE reconnect success"
	else
		put_cr_log "$DEVICE reconnect fail: ${result}"
		return 1
	fi
	return 0
}

is_connected() {
	if ping -s 1 -c "${PING_COUNT}" -I "${NETWORK_IF}" "${PING_DEST_IP}" > /dev/null 2>&1; then
		return 0
	else
		put_cr_log "$DEVICE ping fail"
	fi
	return 1
}

clear_config() {
	PRODUCT_NAME=""
	CHECK_INTERVAL_SEC=120
	PING_DEST_IP=""
	DEVICE=""
	TYPE=""
	NETWORK_IF=""
	WWAN_FORCE_RESTART_COUNT=10
	REBOOT_IF_SIM_NOT_FOUND=""
}

set_default_config() {
	PRODUCT_NAME=$(cat /proc/device-tree/model)
	CHECK_INTERVAL_SEC=120
	PING_DEST_IP="8.8.8.8"
	DEVICE="ttyCommModem"
	TYPE="gsm"

	if echo "$PRODUCT_NAME" | grep -q "A6E Cat.1 Board"; then
		NETWORK_IF="usb0"
	else
		NETWORK_IF="ppp0"
	fi

	FORCE_REBOOT=FALSE
	REBOOT_IF_SIM_NOT_FOUND=FALSE
	WWAN_FORCE_RESTART_COUNT=10
}

read_config_file() {
	if [ ! -e "$CONFIG_FILE" ]; then
		return 1
	fi

	. "${CONFIG_FILE}"

	if [ -z "$PING_DEST_IP" ]; then
		put_cr_log "$CONFIG_FILE is PING_DEST_IP not set"
		exit 1
	fi
	if [ -z "$FORCE_REBOOT" ]; then
		put_cr_log "$CONFIG_FILE is FORCE_REBOOT not set"
		exit 1
	fi
	if [ -z "$CHECK_INTERVAL_SEC" ]; then
		put_cr_log "$CONFIG_FILE is CHECK_INTERVAL_SEC not set"
		exit 1
	fi

	return 0
}

read_old_config_file() {
	if [ ! -e "$CONFIG_FILE" ]; then
		return 1
	fi

	. "${CONFIG_FILE}"

	local CHECK_PRODUCT_FLG=1
	local IFS=","
	for CHECK_PRODUCT in $PRODUCT_NAME; do
		if grep "${CHECK_PRODUCT}" /proc/device-tree/model -q -w; then
			CHECK_PRODUCT_FLG=0
			break
		fi
	done
	if [ ${CHECK_PRODUCT_FLG} -ne 0 ]; then
		return 1
	fi

	if ! read_config_file; then
		exit 1
	fi

	if [ -z "$DEVICE" ]; then
		put_cr_log "$CONFIG_FILE is DEVICE not set"
		exit 1
	fi
	if [ -z "$TYPE" ]; then
		put_cr_log "$CONFIG_FILE is TYPE not set"
		exit 1
	fi
	if [ -z "$NETWORK_IF" ]; then
		put_cr_log "$CONFIG_FILE is NETWORK_IF not set"
		exit 1
	fi

	return 0
}

load_config() {
	local IFS="
"
	# use old conf file first
	if [ -d $OLD_CONFIG_FILE_DIR ]; then
		for CONFIG_FILE in "$OLD_CONFIG_FILE_DIR/"*"$CONFIG_FILE_EXT"; do
			clear_config
			if read_old_config_file; then
				return
			fi
		done
	fi

	set_default_config

	# use "/etc/atmark/connection-recover.conf"
	CONFIG_FILE=$NEW_CONFIG_FILE
	if read_config_file; then
		return
	fi

	set_default_config
}

nmcli_device_connecting() {
	SIM_NOT_FOUND_COUNT=0
	DEVICE_CONNECTING_COUNT=$((DEVICE_CONNECTING_COUNT+1))
	DEVICE_UNMANAGED_COUNT=0
	if [ "$DEVICE_CONNECTING_COUNT" -ge "$FORCE_RECONNECT_DEVICE_CONNECTING_COUNT" ]; then
		put_cr_log "exec wwan-force-restart cause device $DEVICE connecting"
		wwan-force-restart "$CONNECTION"
		WWAN_RESTART_COUNT=0
		DEVICE_CONNECTING_COUNT=0
	fi
}

# $1      cause string for log
nmcli_device_unmanaged_or_not_found() {
	SIM_NOT_FOUND_COUNT=0
	DEVICE_CONNECTING_COUNT=0
	DEVICE_UNMANAGED_COUNT=$((DEVICE_UNMANAGED_COUNT+1))
	if [ "$DEVICE_UNMANAGED_COUNT" -ge "$FORCE_RECONNECT_DEVICE_UNMANAGED_COUNT" ]; then
		put_cr_log "exec wwan-force-restart cause device $DEVICE $1"
		wwan-force-restart "$CONNECTION"
		WWAN_RESTART_COUNT=0
		DEVICE_UNMANAGED_COUNT=0
	fi

	put_cr_log "device $DEVICE $1"
}

clear_counts() {
	SIM_NOT_FOUND_COUNT=0
	DEVICE_UNMANAGED_COUNT=0
	DEVICE_CONNECTING_COUNT=0
}

load_config

PING_NG_COUNT=0
WWAN_RESTART_COUNT=0
clear_counts

if [ -z "$WWAN_FORCE_RESTART_COUNT" ]; then
	WWAN_FORCE_RESTART_COUNT=1
fi

put_cr_trace_log "start."
while true; do

	sleep ${CHECK_INTERVAL_SEC}

	CONNECTION="${TYPE}-${DEVICE}"

	if active_connection_exists; then

		PING_STATUS=1

		STATUS=$(nmcli -f DEVICE,STATE device \
			| awk -v dev="$DEVICE" '$1 == dev {print $2}')
		if [ -z "$STATUS" ]; then
			nmcli_device_unmanaged_or_not_found "not found"
			continue
		fi

		put_cr_trace_log "connection status: ${STATUS}"
		case ${STATUS} in
		*disconnected*|*failed*|*unknown*) # *failed* inclueded "connection failed"
			clear_counts
			if reconnect; then
				continue
			fi ;;
		*connected*)
			clear_counts
			is_connected
			PING_STATUS=$?
			if [ $PING_STATUS -ne 0 ]; then
				# if reboot or restart wwan, do not reconnect
				PING_NG_COUNT_TMP=$((PING_NG_COUNT+1))
				if [ $PING_NG_COUNT_TMP -lt $FORCE_RECONNECT_PING_NG_COUNT ]; then
					reconnect
				fi
			else
				WWAN_RESTART_COUNT=0
			fi ;;
		*connecting*)
			nmcli_device_connecting
			continue ;;
		*deactivating*)
			clear_counts
			continue ;;
		*unmanaged*)
			nmcli_device_unmanaged_or_not_found "unmanaged"
			continue ;;
		*unavailable*|*)
			DEVICE_UNMANAGED_COUNT=0
			DEVICE_CONNECTING_COUNT=0
			if [ "$REBOOT_IF_SIM_NOT_FOUND" != "TRUE" ] ; then
				continue
			fi
			SIM_NOT_FOUND_COUNT=$((SIM_NOT_FOUND_COUNT+1))
			put_cr_log "sim not found. status: ${STATUS}"
			if [ $SIM_NOT_FOUND_COUNT -ge $RESTART_SIM_NOT_FOUND_COUNT ] ; then
				put_cr_log "exec system reboot cause sim not found"
				reboot
			fi
		esac

		if [ $PING_STATUS -ne 0 ]; then
			PING_NG_COUNT=$((PING_NG_COUNT+1))
			if [ "$FORCE_REBOOT" = "TRUE" ]; then
				if [ $PING_NG_COUNT -ge $FORCE_REBOOT_PING_NG_COUNT ]; then
					put_cr_log connection-recover "exec system reboot"
					reboot
				fi
			fi
			if [ $PING_NG_COUNT -ge $FORCE_RECONNECT_PING_NG_COUNT ]; then
				WWAN_RESTART_COUNT=$((WWAN_RESTART_COUNT+1))
				if [ $WWAN_RESTART_COUNT -ge $WWAN_FORCE_RESTART_COUNT ]; then
					put_cr_log "exec wwan-force-restart"
					wwan-force-restart "$CONNECTION"
					WWAN_RESTART_COUNT=0
				else
					put_cr_log "exec wwan-restart"
					wwan-force-restart "$CONNECTION" airplane
				fi
			fi
		else
			PING_NG_COUNT=0
		fi
	else
		put_cr_trace_log "no active connection"
	fi
done
