#!/bin/bash
################################################################################
# Copyright 2026 ModalAI Inc.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
#    may be used to endorse or promote products derived from this software
#    without specific prior written permission.
#
# 4. The Software is used solely in conjunction with devices provided by
#    ModalAI Inc.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
################################################################################

source /home/root/.profile.d/modalai_sku_definitions.sh


RESET_ALL="\e[0m"
RED="\e[91m"
YLW="\e[33m"
GRN="\e[32m"
SET_BOLD="\e[1m"


PRINT_ERROR (){
	echo -e "${RED}[ERROR] $@${RESET_ALL}"
}

PRINT_GREEEN_LINE (){
	echo -e "${GRN}${SET_BOLD}------------------------------------------------------------------${RESET_ALL}"
}


# list of required calibration files to check at the end of setup
BASE="/data/modalai/"
REQUIRED_CAL_FILES=()

## imu thermal cal is not particularly useful on VOXL2 ICM42688 but is useful
## on BMI270 so turn on by default for QCS6490 platforms. This can be turned off
## later on speific skus like test jigs
IS_IMU_TEMP_REQUIRED=false
MISSING_REQUIRED_IMU_TEMP_CAL=false
CHIP=$(voxl-chip)
if [ $CHIP == "QCS6490" ]; then
	IS_IMU_TEMP_REQUIRED=true
fi

PX4_DIR="/data/px4/param/"
PX4_CAL_SET_NOMAG=()
PX4_CAL_SET_NOMAG+=("${PX4_DIR}parameters_gyro.cal")
PX4_CAL_SET_NOMAG+=("${PX4_DIR}parameters_acc.cal")
PX4_CAL_SET_NOMAG+=("${PX4_DIR}parameters_level.cal")
PX4_CAL_SET_NOMAG+=("${PX4_DIR}parameters_baro_tc.cal")

PX4_CAL_SET=(${PX4_CAL_SET_NOMAG[@]})
PX4_CAL_SET+=("${PX4_DIR}parameters_mag.cal")

# seeker v6 has our new GPS with DPS baro, no need to baro temp cal
PX4_CAL_SET_SEEKER_V6=()
PX4_CAL_SET_SEEKER_V6+=("${PX4_DIR}parameters_gyro.cal")
PX4_CAL_SET_SEEKER_V6+=("${PX4_DIR}parameters_acc.cal")
PX4_CAL_SET_SEEKER_V6+=("${PX4_DIR}parameters_level.cal")


## local mode variables
QUIET=false
USE_JSON=false
MISSING_CAL=false

print_usage () {
	echo -e ""
	echo -e "Tool to check if all required calibration files are present."
	echo -e "It reads $SKU_FILENAME to determine which kit the VOXL"
	echo -e "is configured for and decides what cal files are needed."
	echo -e ""
	echo -e "To use in interactive mode:"
	echo -e $GRN "~$ voxl-check-calibration" $RESET_ALL
	echo -e "This will print a human readbale report. It is called at"
	echo -e "the end of voxl-configure-mpa"
	echo -e ""
	echo -e "To use in non-interactive mode use either:"
	echo -e $GRN "~$ voxl-check-calibration -q" $RESET_ALL
	echo -e $GRN "~$ voxl-check-calibration --quiet" $RESET_ALL
	echo -e "This will print nothing but still returns 0 or 1"
	echo -e "to indicate pass or fail like interactive mode."
	echo -e ""
	echo -e "To print this help message:"
	echo -e $GRN "~$ voxl-check-calibration -h" $RESET_ALL
	echo -e $GRN "~$ voxl-check-calibration --help" $RESET_ALL
	echo -e ""
	exit 0
}


_check_imu_temp_cal()
{
	local file="/data/modalai/voxl-imu-server.cal"
	local string_to_check="\"has_temp_cal_new0\":	true"

	if [ ! -f "$file" ]; then
		# File is missing
		MISSING_REQUIRED_IMU_TEMP_CAL=true
	elif grep -q "$string_to_check" "$file"; then
		# File exists and contains the string
		MISSING_REQUIRED_IMU_TEMP_CAL=false
	else
		# File exists but does not contain the string
		MISSING_REQUIRED_IMU_TEMP_CAL=true
	fi
}


_make_list(){
	case "$VOXL_FAMILY_CODE" in

		"MDK-F0001"|"MDK-F0002"|"MRB-D0001") # VOXL1 Flight Deck and M500
			REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_intrinsics.yml")
			REQUIRED_CAL_FILES+=("${BASE}opencv_stereo_intrinsics.yml")
			REQUIRED_CAL_FILES+=("${BASE}opencv_stereo_extrinsics.yml")
			;;

		"MCM-C0001"|"MRB-D0003") # voxlcam & seeker-legacy v1 tof+stereo+tracking
			REQUIRED_CAL_FILES+=("${BASE}voxl-imu-server.cal")
			REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_intrinsics.yml")
			# only require stereo cal on the version with the stereo pair
			if [ "$VOXL_CAM_NUM" == "7" ]; then
				REQUIRED_CAL_FILES+=("${BASE}opencv_stereo_intrinsics.yml")
				REQUIRED_CAL_FILES+=("${BASE}opencv_stereo_extrinsics.yml")
			fi
			;;

		"MRB-D0004"|"MRB-D0006"|"MDK-F0006") # RB5, VOXL2 deck and Sentinel
			REQUIRED_CAL_FILES+=("${BASE}voxl-imu-server.cal")
			REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_intrinsics.yml")
			REQUIRED_CAL_FILES+=("${BASE}opencv_stereo_front_intrinsics.yml")
			REQUIRED_CAL_FILES+=("${BASE}opencv_stereo_front_extrinsics.yml")
			REQUIRED_CAL_FILES+=("${BASE}opencv_stereo_rear_intrinsics.yml")
			REQUIRED_CAL_FILES+=("${BASE}opencv_stereo_rear_extrinsics.yml")
			REQUIRED_CAL_FILES+=(${PX4_CAL_SET[@]})
			;;

		"MRB-D0005"|"MRB-D0011") # starling 1 and PX4 Autonomy dev kit
			REQUIRED_CAL_FILES+=("${BASE}voxl-imu-server.cal")
			if [ "$VOXL_CAM_NUM" == "25" ]; then
				REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_front_intrinsics.yml")
				REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_down_intrinsics.yml")
			else
				REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_intrinsics.yml")
			fi
			REQUIRED_CAL_FILES+=(${PX4_CAL_SET[@]})
			;;

		"MRB-D0008" | "MRB-D0019" | "MRB-D0020") # seeker Vision 5 7 10
			REQUIRED_CAL_FILES+=("${BASE}voxl-imu-server.cal")


			if [ $VOXL_HW_VERSION == "6" ]; then
				REQUIRED_CAL_FILES+=(${PX4_CAL_SET_SEEKER_V6[@]})
				if [[ "$VOXL_CAM_NUM" != "50" && "$VOXL_CAM_NUM" != "60" ]]; then
					REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_front_intrinsics.yml")
					REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_down_intrinsics.yml")
					REQUIRED_CAL_FILES+=("${BASE}opencv_lepton0_raw_intrinsics.yml")
				fi

			elif [ "$VOXL_HW_VERSION" == "5" ]; then
				REQUIRED_CAL_FILES+=(${PX4_CAL_SET_NOMAG[@]})
				if [[ "$VOXL_CAM_NUM" != "50" && "$VOXL_CAM_NUM" != "60" ]]; then
					REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_front_intrinsics.yml")
					REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_down_intrinsics.yml")
					REQUIRED_CAL_FILES+=("${BASE}opencv_lepton0_raw_intrinsics.yml")
				fi

			elif [ "$VOXL_HW_VERSION" == "4" ]; then
				REQUIRED_CAL_FILES+=(${PX4_CAL_SET_NOMAG[@]})
				REQUIRED_CAL_FILES+=("${BASE}opencv_trackingL_intrinsics.yml")
				REQUIRED_CAL_FILES+=("${BASE}opencv_trackingR_intrinsics.yml")
				REQUIRED_CAL_FILES+=("${BASE}opencv_lepton0_raw_intrinsics.yml")
			fi

			;;

		"MRB-D0010")
			REQUIRED_CAL_FILES+=("${BASE}voxl-imu-server.cal")
			REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_intrinsics.yml")
			REQUIRED_CAL_FILES+=(${PX4_CAL_SET_NOMAG[@]})
			;;

		"MRB-D0012"|"MRB-D0014") # Starling 2 and Starling 2 Max
			REQUIRED_CAL_FILES+=("${BASE}voxl-imu-server.cal")
			REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_front_intrinsics.yml")
			REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_down_intrinsics.yml")
			if [ "$VOXL_CAM_NUM" == "27" ]; then
				REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_rear_intrinsics.yml")
			fi
			if [[ "$VOXL_SKU" == *X8* ]]; then
				REQUIRED_CAL_FILES+=("${BASE}opencv_lepton0_raw_intrinsics.yml")
			fi
			REQUIRED_CAL_FILES+=(${PX4_CAL_SET[@]})
			;;

		"MRB-D0013") # stinger
			REQUIRED_CAL_FILES+=("${BASE}voxl-imu-server.cal")
			REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_front_intrinsics.yml")
			REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_down_intrinsics.yml")
			REQUIRED_CAL_FILES+=("${BASE}opencv_lepton0_raw_intrinsics.yml")
			REQUIRED_CAL_FILES+=(${PX4_CAL_SET_NOMAG[@]})
			;;

		"MRB-D0015") #
			REQUIRED_CAL_FILES+=("${BASE}voxl-imu-server.cal")
			REQUIRED_CAL_FILES+=(${PX4_CAL_SET[@]})
			;;

		"MRB-D0016") # Sparrow
			REQUIRED_CAL_FILES+=("${BASE}voxl-imu-server.cal")
			REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_front_intrinsics.yml")
			REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_down_intrinsics.yml")
			REQUIRED_CAL_FILES+=("${BASE}opencv_tracking_rear_intrinsics.yml")
			REQUIRED_CAL_FILES+=(${PX4_CAL_SET_NOMAG[@]})
			;;

		"MRB-D0017") # shikra
			REQUIRED_CAL_FILES+=("${BASE}voxl-imu-server.cal")
			REQUIRED_CAL_FILES+=(${PX4_CAL_SET_NOMAG[@]})
			;;

		"TF-M0054") # voxl2 test fixture
			IS_IMU_TEMP_REQUIRED=false
			;;
		
		"TF-M0104") # voxl2-mini test fixture
			IS_IMU_TEMP_REQUIRED=false
			;;

		"MCCA-M0054") # voxl2 board only
			REQUIRED_CAL_FILES+=("${BASE}voxl-imu-server.cal")
			REQUIRED_CAL_FILES+=(${PX4_CAL_SET[@]})
			;;
		
		"MCCA-M0104") # voxl2-mini board only
			REQUIRED_CAL_FILES+=("${BASE}voxl-imu-server.cal")
			REQUIRED_CAL_FILES+=(${PX4_CAL_SET[@]})
			;;

		"MVX-T0001" | "MVX-R0001" | "MVX-T0002" | "MVX-R0002" | "MVX-H0001" | "MVX-S0001")
			IS_IMU_TEMP_REQUIRED=false
			;;

		*)
			PRINT_ERROR "ERROR in voxl-check-calibration: Unknown Family Code: ${VOXL_FAMILY_CODE}"
			exit 1
			;;
	esac

	if [ "$VOXL_MODEM_NUM" == "26" ] || [ "$VOXL_MODEM_NUM" == "2026" ] || [ "$VOXL_MODEM_NUM" == "2126" ]; then
		REQUIRED_CAL_FILES+=("${BASE}vtx/calibrated_2_4.csv")
		REQUIRED_CAL_FILES+=("${BASE}vtx/calibrated_5_1.csv")
		REQUIRED_CAL_FILES+=("${BASE}vtx/calibrated_5_8.csv")
	fi

}


_check_list ()
{
	if !(( ${#REQUIRED_CAL_FILES[@]} )); then
		echo -e "${GRN}${SET_BOLD}No Calibration Files Neccesary for ${VOXL_FAMILY_NAME}${RESET_ALL}"
		exit 0
	fi

	MISSING_CAL=false
	COLOR=$GRN

	## check any required files
	for i in ${REQUIRED_CAL_FILES[@]}; do
		if ! [ -f "$i" ]; then
			MISSING_CAL=true
			COLOR=$YLW
		fi
	done

	## check for temp cal if required
	if $IS_IMU_TEMP_REQUIRED ; then
		_check_imu_temp_cal
		if $MISSING_REQUIRED_IMU_TEMP_CAL; then
			COLOR=$YLW
			MISSING_CAL=true
		fi
	fi

	## in quiet mode we can just return here with the result
	if $QUIET; then
		return
	fi

	if ! $USE_JSON ; then
		echo -e "${COLOR}${SET_BOLD}Calibration File Status:${RESET_ALL}"
	fi


	## construct our list of present and missing files, either printing to the
	## terminal or adding to the list for JSON output
	present_files=()
	missing_files=()

	for i in ${REQUIRED_CAL_FILES[@]}; do
		if [ -f "$i" ]; then
			if $USE_JSON ; then
				present_files+=("\"$i\"")
			else
				echo -e ${GRN}${SET_BOLD}Present $i ${RESET_ALL}
			fi
		else
			if $USE_JSON ; then
				missing_files+=("\"$i\"")
			else
				echo -e ${YLW}${SET_BOLD}Missing $i ${RESET_ALL}
			fi
		fi
	done


	# Check IMU temp cal requirements and append to appropriate list
	if $IS_IMU_TEMP_REQUIRED; then
		if $MISSING_REQUIRED_IMU_TEMP_CAL; then
			if $USE_JSON; then
				missing_files+=("\"imu_temp_cal\"")
			else
				echo -e ${YLW}${SET_BOLD}Missing imu temp cal${RESET_ALL}
			fi
		else
			if $USE_JSON; then
				present_files+=("\"imu_temp_cal\"")
			else
				echo -e ${GRN}${SET_BOLD}Present imu temp cal${RESET_ALL}
			fi
		fi
	fi


	## print the final result in JSON if required
	if $USE_JSON ; then
		echo -n "{ \"Present\": [$(IFS=,; echo "${present_files[*]}")], \"Missing\": [$(IFS=,; echo "${missing_files[*]}")]}"
		return
	fi

	## normal prints
	if $MISSING_CAL ; then
		echo -e ""
		echo -e "${YLW}${SET_BOLD}Detected Missing Calibration Files${RESET_ALL}"
		echo -e "${YLW}${SET_BOLD}please run the required cals${RESET_ALL}"
		echo -e "${YLW}${SET_BOLD}https://docs.modalai.com/calibration/${RESET_ALL}"
	else
		echo -e ""
		echo -e "${GRN}${SET_BOLD}All Required Calibration Files Present${RESET_ALL}"
	fi

	return
}


_main(){

	while (( "$#" )); do
		case "$1" in

		"-q"|"--quiet")
			QUIET=true
			shift
			;;

		"-h"|"--help")
			print_usage
			exit 0
			;;

		"--json"|"-j")
			USE_JSON=true
			shift
			;;

		*)
			echo "Invalid arg: $1, exiting"
			exit -1
			;;

		esac
	done

	## do a quick parse, this will migrate old filename to new in case
	if ! voxl-inspect-sku --quiet; then
		exit 1
	fi

	## read from file and populate SKU variables
	VOXL_SKU=$( cat $SKU_FILENAME )
	if ! voxl-parse-and-export-sku-variables $VOXL_SKU; then
		exit 1
	fi

	_make_list
	_check_list

	if $MISSING_CAL; then
		exit 1
	fi
	exit 0
}


_main "$@"
