#!/bin/sh -e
# Copyright (C) 2015-2019 Axis Communications AB

readonly rclib=/lib/rcscripts/sh
. $rclib/rc-std2parse.sh
. $rclib/files.sh

readonly persistent_dir=/lib/persistent/var/lib/focusd
readonly conf_dir=/usr/share/focusd
readonly state_file=$persistent_dir/focusd.state
readonly state_file_tmp=$state_file.tmp
readonly fcf=/etc/focusd/focusd_positions.conf
readonly fcf_tmp=$fcf.tmp
readonly sffile=/etc/sysconfig/imagesource_focus_positions.conf
readonly newcf=$conf_dir/motors.conf

# adjust_focus:
# @$1: name of variable to return adjusted position value in
# @$2: old position
# @$3: old near limit
# @$4: new near limit
# @$5: new length
adjust_focus() {
	local np
	[ $# -eq 5 ] || error "$0: adjust_focus: bad arguments '$*'"
	# Adjust focus
	# newpos = oldpos + newnear - oldnear
	np=$(($2 + $4 - $3))
	[ $np -ge 0 ] || np=0
	[ $np -lt $5 ] || np=$(($5 - 1))
	eval $1=$np
}

# adjust_float:
# @$1: name of variable to return adjusted position value in
# @$2: old position
# @$3: old length
# @$4: old near limit
# @$5: new length
# @$6: new near limit
adjust_float() {
	local npf oldp
	[ $# -eq 6 ] || error "$0: adjust_float: bad arguments '$*'"
	oldp=$(echo "$2 $3 * p" | dc)
	oldp=${oldp%.*}
	# Calculate a new value relative value
	adjust_focus npf $oldp $4 $6 $5
	npf=$(echo "$npf $5 / p" | dc)
	eval $1=$npf
}

# get_config:
# @$1: the config file name
# @$2: the optics type of which to read the config (upper case)
# @$3: name of the variable to return the controller type in
# @$4: name of the variable to return the length value in
# @$5: name of the variable to return the min_nearlimit_dioptre value in
# @$6: name of the variable to return the infinity_fudge_margin value in
get_config() {
	local cfg c cfg_mc='' cfg_l='' cfg_nl='' cfg_m='' IFS
	[ $# -eq 6 ] || error "$0: get_config: bad arguments '$*'"

	cfg=$(sed -ne "
/^\[features\]$/,/^\[.*/ s/^motorcontroller=\(.*\)$/cfg_mc=\1/p

/^\[focus_$2\]$/,/^\[.*/ {
	s/^length=\(.*\)$/cfg_l=\1/p
	s/^min_nearlimit_dioptre=\(.*\)$/cfg_nl=\1/p
	s/^infinity_fudge_margin=\(.*\)$/cfg_m=\1/p
}
" $1) ||
		return 1

	IFS='
'
	for c in $cfg; do
		case $c in
			cfg_mc=*)
				cfg_mc=${c#*=}
				;
			cfg_l=*)
				cfg_l=${c#*=}
				;
			cfg_nl=*)
				cfg_nl=${c#*=}
				;
			cfg_m=*)
				cfg_m=${c#*=}
				;
		esac
	done

	eval $3=\$cfg_mc
	eval $4=\$cfg_l
	eval $5=\$cfg_nl
	eval $6=\$cfg_m
}

# move_old_backup:
# @$1: the old config file name
move_old_backup() {
	local l os= ns= bf
	[ $# -eq 1 ] && [ "$1" ] || error "$0: move_old_backup, bad inputs '$*'"
	[ -e $1 ] || return 0
	bf=$1.orig
	while read l; do
		case $l in
			\[*_override\])
				os=y
				;
			\[*\])
				ns=y
				;
		esac
	done <$1
	[ -e $bf ] || {
		cp $1 $bf || warning "$0: failed to copy '$1' to '$bf'"
		chmod a-w $bf || warning "$0: failed to chmod '$bf'"
	}
	[ "$os" = y ] || {
		rm $1 || warning "$0: failed to remove '$1'"
		return 0
	}
	chmod a-w $1 || warning "$0: failed to chmod '$1'"
	[ "$ns" != y ] ||
		warning "$0: '$1' contains both override and non-override sections"
}

# configs_are_equal:
# @$1: config file name
# @$2: config file name
# return: success if files checksum match
configs_are_equal() {
	local first_conf= second_conf=
	[ $# -eq 2 ] && [ "$1" ] && [ "$2" ] ||
		error "$0: configs_are_equal, bad inputs '$*'"
	[ -f $1 ] && [ -f $2 ] ||
		error "$0: configs_are_equal, '$1' or '$2' is not a file"
	first_conf=$1
	second_conf=$2
	set -- $(md5sum $first_conf $second_conf 2>/dev/null || :)
	[ $# -eq 4 ] || error "$0: configs_are_equal, files comparison failed"
	[ $1 = $3 ]
}

[ -r  $newcf ] || error "$0: no readable '$newcf' file"

hwid=$(bootblocktool -x HWID)
[ "$hwid" ] || error "$0: no HWID in bootblock"
hwid=${hwid%%.*}
readonly hwid
backup_file=$persistent_dir/motors.conf.backup
backup_file_tmp=$backup_file.tmp

case $hwid in
	1B0|1F4|700)
		backup_file=$persistent_dir/motors.conf
		backup_file_tmp=$backup_file.tmp
		;
	726|742)
		move_old_backup $persistent_dir/motors.conf
		configs_are_equal $newcf $backup_file || {
			cp -pf $newcf $backup_file_tmp
			fsynced_write_or_cleanup $backup_file_tmp $backup_file
		}
		exit 0
		;
	*)
		move_old_backup $persistent_dir/motors.conf
		;
esac
readonly backup_file backup_file_tmp

[ -f $backup_file ] || {
	readonly base_cf=$conf_dir/base/$hwid.conf
	file=$newcf
	[ ! -f $base_cf ] || file=$base_cf
	cp -pf $file $backup_file_tmp
	fsynced_write_or_cleanup $backup_file_tmp $backup_file
}

! configs_are_equal $newcf $backup_file || {
	information "$0: configuration files match, no adjustment"
	exit 0
}

optics=$(bootblocktool -x OPTICS)
readonly optics
[ "$optics" ] || error "$0: no OPTICS in bootblock"

# read config file
get_config $newcf $optics new_ctrl new_len new_nl new_ifm
readonly new_ctrl new_len new_nl new_ifm
[ "$new_ctrl" ] || error "$0: no motorcontroller data in '$newcf'"
[ "$new_ctrl" = image2d ] || {
	information "$0: no focus adjustment for controller '$new_ctrl'"
	exit 0
}
[ "$new_len" ] && [ "$new_nl" ] && [ "$new_ifm" ] ||
	error "$0: missing values in the new configuration"

# read old config file
get_config $backup_file $optics old_ctrl old_len old_nl old_ifm
readonly old_len old_nl old_ifm
[ "$old_len" ] && [ "$old_nl" ] && [ "$old_ifm" ] ||
	error "$0: missing values in the old configuration"

# finished with old configuration, backup new
cp -pf $newcf $backup_file_tmp
fsynced_write_or_cleanup $backup_file_tmp $backup_file

[ $old_len -ne $new_len ] ||
	[ $old_nl -ne $new_nl ] ||
	[ $old_ifm -ne $new_ifm ] || {
		information "$0: old and new length, near limit, and infinity margin match"
		exit 0
	}

# adjust state
std2parse $state_file || error "$0: parsing '$state_file' failed"
cp -pf $state_file $state_file_tmp
[ -z "$STD2_MECHANICS_FOCUS_MOTORPOSITION" ] ||
	[ $STD2_MECHANICS_FOCUS_MOTORPOSITION -eq -1 ] || {
		adjust_focus val $STD2_MECHANICS_FOCUS_MOTORPOSITION $old_nl $new_nl $new_len
		sed -i "/[[]Mechanics.Focus[]]/,/MotorPosition=.*/ s/MotorPosition=.*/MotorPosition=$val/" $state_file_tmp || {
			rm -f $state_file_tmp
			error "$0: updating $state_file failed"
		}
		fsynced_write_or_cleanup $state_file_tmp $state_file
	}

# adjust LUPosition
std2parse $fcf || error "$0: parsing '$fcf' failed"
cp -pf $fcf $fcf_tmp
[ -z "$STD2_MECHANICS_FOCUS_LUPOSITION" ] ||
	[ $STD2_MECHANICS_FOCUS_LUPOSITION -eq -1 ] || {
		adjust_focus lupf $STD2_MECHANICS_FOCUS_LUPOSITION $old_nl $new_nl $new_len
		sed -i "/[[]Mechanics.Focus[]]/,/LUPosition=.*/ s/LUPosition=.*/LUPosition=$lupf/" $fcf_tmp || {
			rm -f $fcf_tmp
			error "$0: updating $fcf failed"
		}
		fsynced_write_or_cleanup $fcf_tmp $fcf
	}

# adjust saved position
std2parse $sffile || error "$0: parsing '$sffile' failed"
[ -z "$STD2_IMAGESOURCE_I0_FOCUS_POS" ] || {
	adjust_float new_pos_f $STD2_IMAGESOURCE_I0_FOCUS_POS $old_len $old_nl $new_len $new_nl
	sed -i "/[[]ImageSource.I0.Focus[]]/,/Pos=.*/ s/Pos=.*/Pos=$new_pos_f/" $sffile ||
		error "$0: updating $sffile failed"
}