#!/bin/bash

CONFIG_DEV_CHANGE=0
ASK_TO_REBOOT=0

SERV_HOME=$(dirname $0)
. ${SERV_HOME}/iotg-rpi4-functions

UNRECOGNIZED_ERR="Programmer error: unrecognized option"
RUNNING_ERR="There was an error running option"

#############################################################################
calc_wt_size() { # Taken from raspi-config
	# NOTE: it's tempting to redirect stderr to /dev/null, so supress error 
	# output from tput. However in this case, tput detects neither stdout or 
	# stderr is a tty and so only gives default 80, 24 values
	WT_HEIGHT=18
	WT_WIDTH=$(tput cols)

	if [ -z "$WT_WIDTH" ] || [ "$WT_WIDTH" -lt 60 ]; then
		WT_WIDTH=80
	fi
	if [ "$WT_WIDTH" -gt 178 ]; then
		WT_WIDTH=120
	fi
	WT_MENU_HEIGHT=$(($WT_HEIGHT-7))
}

#############################################################################
# Apply user defined configuration by putting appropriate lines into config.txt
#############################################################################
do_apply_config() {
	if [ ! -f ${CONFIG} ]; then
		whiptail --title "Failure" --msgbox "\
		Cannot apply configuration changes:
		${CONFIG} file not found" 10 60 1
		exit 1
	fi
	## Prepare IOTG-PRi4 config
	: > ${IOTG_RPI4_CONFIG}
	### Open IOTG-RPi4 section
	IOTG_RPI4_CUTLINE="######## iotg-rpi4 configuration"
	echo "${IOTG_RPI4_CUTLINE} - Begin" >> ${IOTG_RPI4_CONFIG}

	### Apply IE slots configuration: for each slot generate a line to be written into config.txt
	for slot in ${IE_SLOT_LCASE[@]}; do
		SLOT_CFG_STR=
		case ${IE_CONFIG[${slot}]} in
		"Keep")
			#### Copy from the previous version of config.txt
			SLOT_CFG_STR=$(grep -i "${IOTG_RPI4_PATTERN}-ie-${slot}" ${CONFIG})
			;;
		"CAN")
			#### Enable CAN
			SLOT_CFG_STR="dtoverlay=iotg-rpi4/iotg-rpi4-ie-${slot},can"
			;;
		"RS232" | "RS485")
			#### Enable UART
			SLOT_CFG_STR="dtoverlay=iotg-rpi4/iotg-rpi4-ie-${slot},uart"
			;;
		"Detect" | "Deactivate")
			#### Use detected IE type
			case ${IE_DETECTED[${slot}]} in
			"CAN")
				#### CAN
				SLOT_CFG_STR="dtoverlay=iotg-rpi4/iotg-rpi4-ie-${slot},can"
				;;
			"RS232" | "RS485")
				#### UART
				SLOT_CFG_STR="dtoverlay=iotg-rpi4/iotg-rpi4-ie-${slot},uart"
				;;
			"IO" | *)
				#### Do nothing 
				SLOT_CFG_STR=
				continue
				;;
			esac
			if [[ -n ${SLOT_CFG_STR} ]]; then
				if [[ "${IE_CONFIG[${slot}]}" == "Deactivate" ]]; then				
					#### Deactivate: comment out the line
					SLOT_CFG_STR="#"${SLOT_CFG_STR}
				else
					#### Detect: put the detected IE type into user conigured array
					IE_CONFIG[${slot}]=${IE_DETECTED[${slot}]}
				fi
			fi
			;;
		"Empty" | *)
			#### Do nothing
			SLOT_CFG_STR=
			;;
		esac
		#
		[[ -z ${SLOT_CFG_STR} ]] || echo ${SLOT_CFG_STR} >> ${IOTG_RPI4_CONFIG}
	done

	SLOT_CFG_STR=
	slot_list=
	for dslot in ${DUAL_IE_SLOT_LCASE[@]}; do
		#SLOT_CFG_STR="dtoverlay=iotg-rpi4/iotg-rpi4-dio"
		case ${DUAL_IE_CONFIG[${dslot}]} in
		"Keep")
			#### Copy from the previous version of config.txt
			if [[ "$(grep -ic -E "${IOTG_RPI4_PATTERN}-dio,.*${dslot}" ${CONFIG})" == "1" ]]; then
				SLOT_CFG_STR="dtoverlay=iotg-rpi4/iotg-rpi4-dio"
				slot_list+=",${dslot}"
			fi
			;;
		"IO")
			SLOT_CFG_STR="dtoverlay=iotg-rpi4/iotg-rpi4-dio"
			slot_list+=",${dslot}"
			;;
		"Detect")
			case ${DUAL_IE_DETECTED[${slot}]} in
			"IO")
				SLOT_CFG_STR="dtoverlay=iotg-rpi4/iotg-rpi4-dio"
				slot_list+=",${dslot}"
				;;
			*)
				#### Do nothing
				;;
			 esac
			;;
		*)
			#### Do nothing
			;;
		esac
	done
	SLOT_CFG_STR+="${slot_list}"
	[[ -z ${SLOT_CFG_STR} ]] || echo ${SLOT_CFG_STR} >> ${IOTG_RPI4_CONFIG}

	### Apply peripheral devices configuration
	#### Disable TPM if required by IE-D module type
	if [[ "${IE_CONFIG["d"]}" == "RS232" || "${IE_CONFIG["d"]}" == "RS485" || "${DUAL_IE_CONFIG["cd"]}" == "IO" ]]; then
		PERIPHERAL_DEVS[TPM]="OFF"
		CONFIG_DEV_CHANGE=1;
	fi
	#### Set configurable peripherals
	if [[ ${CONFIG_DEV_CHANGE} -eq 1 ]]; then
		#### Set TPM depending on and user configuration
		TPM_CFG="dtoverlay=iotg-rpi4/iotg-rpi4-tpm"
		if [[ "${PERIPHERAL_DEVS[TPM]}" == "OFF" ]]; then
			TPM_CFG="#"${TPM_CFG}
		fi
		echo "$TPM_CFG" >> ${IOTG_RPI4_CONFIG}
	else
		#### Copy TPM setting from current config.txt version
		grep -i "${IOTG_RPI4_PATTERN}-tpm" ${CONFIG} >> ${IOTG_RPI4_CONFIG}
	fi

	### Unconditional peripherals
	#### EEPROM
	EEPROM_EN="dtoverlay=iotg-rpi4/iotg-rpi4-eeprom"
	echo "$EEPROM_EN" >> ${IOTG_RPI4_CONFIG}
	#### RTC
	RTC_EN="dtoverlay=iotg-rpi4/iotg-rpi4-rtc"
	echo "$RTC_EN" >> ${IOTG_RPI4_CONFIG}
	#### GPIO Expanders
	GPIO_EXP_EN="dtoverlay=iotg-rpi4/iotg-rpi4-gpio-exp"
	echo "$GPIO_EXP_EN" >> ${IOTG_RPI4_CONFIG}

	### Close IOTG-RPi4 section
	echo "${IOTG_RPI4_CUTLINE} - End" >> ${IOTG_RPI4_CONFIG}

	### Remove previous IOTG-RPi4 section from the config.txt
	grep -iv "${IOTG_RPI4_PATTERN}" ${CONFIG} > ${NEW_CONFIG}
	## Add the new IOTG-RPi4 section
	if [ -f ${IOTG_RPI4_CONFIG} ]; then
		cat ${IOTG_RPI4_CONFIG} >> ${NEW_CONFIG}
	fi

	### Backup the previous config.txt
	cp ${CONFIG} ${CONFIG_BACKUP}
	### Put the new config.txt to /boot directory
	mv ${NEW_CONFIG} ${CONFIG}

	## Remove temp files
	[[ ! -f ${IOTG_RPI4_CONFIG} ]] || rm ${IOTG_RPI4_CONFIG} 

	ASK_TO_REBOOT=1
}

#############################################################################
# Ask for reboot if required
#############################################################################
do_finish() {
	if [ $ASK_TO_REBOOT -eq 1 ]; then
		whiptail --yesno "\
		NOTE: ANY CHANGES WILL TAKE EFFECT ONLY AFTER REBOOT.

		Would you like to reboot now?" 10 70 2
		if [ $? -eq 0 ]; then # yes
			sync
			reboot
		fi
	fi
	exit 0
}

#############################################################################
# Exit w/o saving
#############################################################################
do_exit_nosave() {
	whiptail --yesno "Would you like to exit without applying changes?" --yes-button Exit --no-button Back --defaultno 20 60 2
	RET=$?
	if [ $RET -eq 0 ]; then
		ASK_TO_REBOOT=0
		do_finish
	fi	
}

#############################################################################
# Apply configuration and exit
#############################################################################
do_exit_save() {
	whiptail --yesno "Would you like to apply changes and exit?" --yes-button "Save & Exit" --no-button Back 20 60 2
	RET=$?
	if [ $RET -eq 0 ]; then
		# Apply new configuration
		do_apply_config
		# Reboot
		do_finish
	fi
}

#############################################################################
# Peripheral devices configuration menu
#############################################################################
do_peripherals_menu () {
	selected_dev_descr=$(whiptail --title "Configure IOTG-RPI4 Peripherals" --separate-output --checklist "" 20 80 11 \
	"TPM" "Enable/Disable Trusted Platform Module" ${PERIPHERAL_DEVS[TPM]} \
	3>&1 1>&2 2>&3)
	mapfile -t selected_dev_list <<< "$selected_dev_descr"

	for key in "${!PERIPHERAL_DEVS[@]}"; do
		PERIPHERAL_DEVS[${key}]=OFF
	done
	for key in "${selected_dev_list[@]}" ; do
		[[ ! -z "$key" ]] || continue
		PERIPHERAL_DEVS[${key}]=ON
	done
	CONFIG_DEV_CHANGE=1

	return 0
}

#############################################################################
# Display detected I/O modules
#############################################################################
do_ie_show () {
	# Detect
	get_ie_type_all
	# Show
	whiptail --title "Industrial I/O modules detected" --msgbox "\
	Slot A:                [${IE_DETECTED["a"]}]

	Slot B:                [${IE_DETECTED["b"]}]

	Slot C:                [${IE_DETECTED["c"]}]

	Slot D:                [${IE_DETECTED["d"]}]
	" 14 50 1

	return 0
}

#############################################################################
# Display user defined I/O modules configuration
#############################################################################
do_ie_config_show () {
	# Show
	whiptail --title "Industrial I/O modules: user defined configuration" --msgbox "\
	Slot A: [${IE_CONFIG["a"]}] - ${SLOT_CONFIG[${IE_CONFIG["a"]}]}

	Slot B: [${IE_CONFIG["b"]}] - ${SLOT_CONFIG[${IE_CONFIG["b"]}]}

	Slot C: [${IE_CONFIG["c"]}] - ${SLOT_CONFIG[${IE_CONFIG["c"]}]}

	Slot D: [${IE_CONFIG["d"]}] - ${SLOT_CONFIG[${IE_CONFIG["d"]}]}
	" 14 90 1
	return 0
}

#############################################################################
# I/O module configuration menu
#############################################################################
do_ie_config () {
	local slot_idx=${1:-}
	[[ ! -z ${slot_idx} ]] || return 1
	[[ $slot_idx -ge 0 ]] && [[ $slot_idx -le 3 ]] || return 1
	local Uslot=${IE_SLOT_UCASE[${slot_idx}]}
	local Lslot=${IE_SLOT_LCASE[${slot_idx}]}
	declare -A slot_cfg=( 
		[Keep]=OFF
		[Detect]=OFF
		[CAN]=OFF
		[RS232]=OFF
		[RS485]=OFF
		[IO]=OFF
		[Empty]=OFF
		[Deactivate]=OFF
		)
	local curr_cfg=${IE_CONFIG[${Lslot}]}
	slot_cfg["${curr_cfg}"]=ON
	new_conf=$(whiptail --title "Industrial I/O Slot ${Uslot}" --separate-output --radiolist "" 20 80 11 \
	"Keep" "${SLOT_CONFIG["Keep"]}" ${slot_cfg["Keep"]} \
	"Detect" "${SLOT_CONFIG["Detect"]}: [${IE_DETECTED[${Lslot}]}]" ${slot_cfg["Detect"]} \
	"CAN" "${SLOT_CONFIG["CAN"]}" ${slot_cfg["CAN"]} \
	"RS232" "${SLOT_CONFIG["RS232"]}" ${slot_cfg["RS232"]} \
	"RS485" "${SLOT_CONFIG["RS485"]}" ${slot_cfg["RS485"]} \
	"IO" "${SLOT_CONFIG["IO"]}" ${slot_cfg["IO"]} \
	"Empty" "${SLOT_CONFIG["Empty"]}" ${slot_cfg["Empty"]} \
	"Deactivate" "${SLOT_CONFIG["Deactivate"]}" ${slot_cfg["Deactivate"]} \
	3>&1 1>&2 2>&3)
	RET=$?
	if [ $RET -eq 1 ] || [ $RET -eq 255 ]; then
		return 0
	fi

	local buddy_Lslot=${BUDDY_SLOT_LCASE[${slot_idx}]}
	local dslot=${DUAL_SLOT_MAP[${Lslot}]}
	case "${new_conf}" in
	"IO")
		# Set buddy slot to same value
		IE_CONFIG[${buddy_Lslot}]=${new_conf}
		# Set dual slot to same value
		DUAL_IE_CONFIG[${dslot}]=${new_conf}
		;;
	"CAN" | "RS232" | "RS485")
		if [[ "${IE_CONFIG[${buddy_Lslot}]}" == "IO" ]]; then
			# Set buddy slot to 'Empty' value
			IE_CONFIG[${buddy_Lslot}]="Empty"
			# Set buddy slot to default value
		fi
		DUAL_IE_CONFIG[${dslot}]="Empty"
		;;
	"Detect" )
		if [[ "${DUAL_IE_CONFIG[${dslot}]}" == "IO" || "${DUAL_IE_DETECTED[${dslot}]}" == "IO" ]]; then
			# Set buddy slot to same value
			IE_CONFIG[${buddy_Lslot}]=${new_conf}
			# Set dual slot to same value
			DUAL_IE_CONFIG[${dslot}]=${new_conf}
		fi
		case "${IE_DETECTED[${Lslot}]}" in
		"Empty" | "CAN" | "RS232" | "RS485")
			DUAL_IE_CONFIG[${dslot}]="Empty"
			;;
		esac
		;;
	"Keep" | "Empty" | "Deactivate")
		if [[ "${DUAL_IE_CONFIG[${dslot}]}" == "IO" || "${DUAL_IE_DETECTED[${dslot}]}" == "IO" ]]; then
			# Set buddy slot to same value
			IE_CONFIG[${buddy_Lslot}]=${new_conf}
			# Set dual slot to same value
			DUAL_IE_CONFIG[${dslot}]=${new_conf}
		fi
		;;
	esac
	# Set slot new value
	IE_CONFIG[${Lslot}]=${new_conf}
}

#############################################################################
# Slots configuration menu
#############################################################################
do_slots_menu () {
	while true; do
		FUN=$(whiptail --title "Industrial I/O Slots Options" --menu "" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
		"S1 Slot A        " "Configre Industrial I/O module type for slot A" \
		"S2 Slot B        " "Configre Industrial I/O module type for slot B" \
		"S3 Slot C        " "Configre Industrial I/O module type for slot C" \
		"S4 Slot D        " "Configre Industrial I/O module type for slot D" \
		3>&1 1>&2 2>&3)
		RET=$?
		if [ $RET -eq 1 ]; then
			return 0
		elif [ $RET -eq 0 ]; then
			case "$FUN" in
			S1\ *) do_ie_config 0 ;;
			S2\ *) do_ie_config 1 ;;
			S3\ *) do_ie_config 2 ;;
			S4\ *) do_ie_config 3 ;;
			*) whiptail --msgbox "${UNRECOGNIZED_ERR}" 20 60 1 ;;
			esac || whiptail --msgbox "${RUNNING_ERR} $FUN" 20 60 1
		fi
	done
}

#############################################################################
# Show configuration menu
#############################################################################
do_ie_show_menu () {
	while true; do
		FUN=$(whiptail --title "Show Industrial I/O Modules Configuration" --menu "" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
		"D1 Show User Conigured        " "Show user defined configuration" \
		"D2 Show Detected        " "Show currently detected configuration" \
		3>&1 1>&2 2>&3)
		RET=$?
		if [ $RET -eq 1 ]; then
			return 0
		elif [ $RET -eq 0 ]; then
			case "$FUN" in
			D1\ *) do_ie_config_show ;;
			D2\ *) do_ie_show ;;
			*) whiptail --msgbox "${UNRECOGNIZED_ERR}" 20 60 1 ;;
			esac || whiptail --msgbox "${RUNNING_ERR} $FUN" 20 60 1
		fi
	done
}

#############################################################################
# I/O modules menu
#############################################################################
do_ie_menu () {
	while true; do
		FUN=$(whiptail --title "Industrial I/O Modules Options" --menu "" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
		"I1 Show" "Show Industrial I/O Modules Configuration" \
		"I2 Configure             " "Configure Industrial I/O Modules Settings" \
		3>&1 1>&2 2>&3)
		RET=$?
		if [ $RET -eq 1 ]; then
			return 0
		elif [ $RET -eq 0 ]; then
			case "$FUN" in
			I1\ *) do_ie_show_menu ;;
			I2\ *) do_slots_menu ;;
			*) whiptail --msgbox "${UNRECOGNIZED_ERR}" 20 60 1 ;;
			esac || whiptail --msgbox "${RUNNING_ERR} $FUN" 20 60 1
		fi
	done
}


# Main routine
## Check user ID
if [[ $(id -u) -ne 0 ]] ; then
	printf "Please run as root: 'sudo $0'\n";
	exit 1
fi

## Check if -quiet flag is arrised
flag=${1:-"-dialog"}
if [[ "${flag}" == "-quiet" ]]; then
	#
	do_apply_config
	exit 0
fi

## Calculate terminal size
calc_wt_size
## Detect all IE modules
get_ie_type_all

#############################################################################
# Main menu
#############################################################################
while true; do
	FUN=$(whiptail --title "IOTG-RPi4 Configuration Tool (iotg-rpi4-config)" --menu "Setup Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT \
	"1 Peripherals" "Peripheral devices options" \
	"2 Industrial I/O modules    " "Industrial I/O modules options" \
	"3 Save Changes and Exit" "Apply changes and exit" \
	"4 Discard Changes and Exit" "Exit discarding changes" \
	3>&1 1>&2 2>&3)
	RET=$?
	if [ $RET -eq 1 ]; then
		do_exit_nosave
    elif [ $RET -eq 0 ]; then
		case "$FUN" in
		1\ *) do_peripherals_menu ;;
		2\ *) do_ie_menu ;;
		3\ *) do_exit_save ;;
		4\ *) do_exit_nosave ;;
		*) whiptail --msgbox "${UNRECOGNIZED_ERR}" 20 60 1 ;;
		esac || whiptail --msgbox "${RUNNING_ERR} $FUN" 20 60 1
	else
		exit 1
	fi
 done
