#!/bin/bash
# Bash arithmetic functions used so don't substitute plain sh
#
# Guralp Configuration System
#   Copyright 2008-2017 Guralp Systems Limited.
#
#   gdi2cd11 CD1.1 compressor/authenticator module

# Handle the navbar query without loading anything
if [ "X$1" == "X--navbar" ]
then
	echo "servicestop	Services"
	echo "servicessub/gdi2cd11	gdi2cd11"
	echo "gdi2cd11/$3	$3"
	exit 0
fi

# Load support functions
script_dir=$(dirname $0)
. $script_dir/functions.sh
. $script_dir/svc_funcs.sh
. $script_dir/gdi-base_link.sh
. $script_dir/cd11mux_link.sh
. $script_dir/ioline.sh

name="$2"
sel="$3"

CFGFILE="$CONFIGDIR/$name/${sel}.local"
CTLFILE="$CONFIGDIR/$name/${sel}.ctl.local"
SRVBASE="$SERVDIR/$name"

# marker used to select "no associated line" for tamper bits/voltage
marker_none="/none"



# readvar()
#  Reads a variable from an existing config file, setting the default to the given value and
#  setting the original to whatever is stored in the config file (if anything).
readvar() {
    local name="$1"
    local section="$2"
    local val="$3"
    local tplname

    if [ -z "${section}" ]
    then
        tplname="${name}"
    else
        tplname="${section}_${name}"
    fi

    gcs_var "d_${tplname}" "${val}"

    if [ -r "$CFGFILE" ]
    then
	val="$(gcs_get_varcf2 "${name}" "${CFGFILE}" "${section}")"
	[ -n "${val}" ] && gcs_var "o_${tplname}" "${val}"
    fi
}



# read_metadata_for_channel()
#  Reads the CD1.1-specific metadata for a given channel.
#  $1 -> channel name
# Array for discarding duplicate channels.
declare -a meta_chans
meta_num=0

read_metadata_for_channel() {
    local src="$1"
    local cd11_instrument_type
    local cd11_calib
    local cd11_calper
    local cname
    local i

    # Skip metadata for SoH channels
    if [ "${src/.SOH.}" != "${src}" ]
    then
	return
    fi

    # Skip duplicates
    cname=$(gcs_gdi_base_get_metadata "${src}" "_FNAME_")

    for (( i = 0 ; i < $meta_num ; i++ ))
    do
	if [ "${meta_chans[$i]}" == "$cname" ]
	then
	    return
	fi
    done

    meta_chans[$meta_num]="$cname"

    gcs_var "o_channel_meta_src${meta_num}" "${src}"
    gcs_var "d_channel_meta_type${meta_num}" "auto"
    gcs_var "d_channel_meta_calib${meta_num}" "1.0"
    gcs_var "d_channel_meta_calper${meta_num}" "1.0"

    eval $(gcs_gdi_base_get_metadata "${src}" \
			"cd11-instrument-type" "cd11-calib" "cd11-calper")
    gcs_var "o_channel_meta_type${meta_num}" "${cd11_instrument_type}"
    gcs_var "o_channel_meta_calib${meta_num}" "${cd11_calib}"
    gcs_var "o_channel_meta_calper${meta_num}" "${cd11_calper}"

    ((++meta_num))
}


do_read() {
    local mapnum entry have_channel_map src dest
    local tran slot auth

    # Standard control info
    gcs_svc_read || return 1

    # Now the service type dependant variables

    # global options
    readvar "data_frame_duration"                       ""          "10"
    readvar "subframe_transformation"                   ""          "compress"
    readvar "spyrus_slot"                               ""          "0"
    readvar "auth_key_id"                               ""          "0"
    readvar "max_clock_differential"                    ""          "500"

    # Warn about authentication parameters with non-signing subframe transform
    if [ -r "$CFGFILE" ]
    then
	tran="$(gcs_get_varcf2 subframe_transformation "${CFGFILE}" "")"
	slot="$(gcs_get_varcf2 spyrus_slot             "${CFGFILE}" "")"
	auth="$(gcs_get_varcf2 auth_key_id             "${CFGFILE}" "")"
	case "$tran" in
	none|compress)
	    if [ "$slot" != "0" ]
	    then
		gcs_warn spyrus_slot "A signing transformation is normally used with enabled authentication."
	    fi
	    if [ -n "$auth" -a "$auth" != "0" ]
	    then
		gcs_warn auth_key_id "A signing transformation is usually selected with an authentication key."
	    fi
	;;
	esac
    fi

    # multiplexor links
    if [ -r "$CFGFILE" ]
    then
	gcs_gdi_base_iselect "$(gcs_get_varcf2 "gdi_socket" "${CFGFILE}" "")" "sink"
	gcs_cd11mux_iselect "$(gcs_get_varcf2 "mux_path" "${CFGFILE}" "")" "source"
    else
	gcs_gdi_base_iselect "" "sink"
	gcs_cd11mux_iselect "" "source"
    fi

    # tamper lines
    ioline_iselect "input" "tamper_lines"
    readvar "equipment_housing_open"                    "tamper"    "${marker_none}"
    readvar "digitizing_equipment_open"                 "tamper"    "${marker_none}"
    readvar "vault_door_opened"                         "tamper"    "${marker_none}"
    readvar "authentication_seal_broken"                "tamper"    "${marker_none}"
    readvar "equipment_moved"                           "tamper"    "${marker_none}"
    readvar "future_use_a"                              "tamper"    "${marker_none}"
    readvar "future_use_b"                              "tamper"    "${marker_none}"
    readvar "future_use_c"                              "tamper"    "${marker_none}"

    # voltage digitisation
    ioline_prop_iselect "float" "float_properties"
    readvar "main_power_failure"                        "voltage"   "${marker_none}"
    readvar "main_power_failure_threshold"              "voltage"   ""
    readvar "main_power_failure_gpio"                   "voltage"   "${marker_none}"
    readvar "backup_power_unstable"                     "voltage"   "${marker_none}"
    readvar "backup_power_unstable_threshold"           "voltage"   ""
    readvar "backup_power_unstable_gpio"                "voltage"   "${marker_none}"
    readvar "future_use_a"                              "voltage"   "${marker_none}"
    readvar "future_use_a_range"                        "voltage"   "current"
    readvar "future_use_b"                              "voltage"   "${marker_none}"
    readvar "future_use_b_range"                        "voltage"   "current"
    readvar "future_use_c"                              "voltage"   "${marker_none}"
    readvar "future_use_c_range"                        "voltage"   "current"

    # Timing drift estimate (information only)
    if [ -d /etc/das-in ]
    then
	# Default needs to match das-in code
	drift_rate=40.0
	if [ -s /etc/das-in/drift-estimate.local ]
	then
	    drift_rate="$(cat /etc/das-in/drift-estimate.local)"
	fi
	gcs_var "d_drift_string" "Estimated drift rate is $drift_rate ns/s."
    fi

    ## channel map
    have_channel_map=0
    mapnum=0
    ignore_unmapped=0

    if [ -r "$CFGFILE" ]
    then
	for entry in $(gcs_list_varcf2_sections "${CFGFILE}")
	do
	    if [ "${entry}" == "channel_map" ]
	    then
		have_channel_map=1
		break
	    fi
	done
    fi

    # detect mapping mode: automatic, in which case we use seedmap (below);
    # semi-automatic, in which case we use both entries in the config file and
    # seedmap; or manual, in which case we only use config entries.
    #
    # Note the tricky "< <(cmd)" syntax after the while loops; this allows us
    # to not have a subshell spawned for the loop body, thus keeping variables
    # intact
    if [ "${have_channel_map}" -eq 0 ]
    then
        # fully automatic mode, don't load anything from config
        gcs_var "o_channel_map_mode" "automatic"
    else
        # manual or semi?
        entry="$(gcs_get_varcf2 "reject_unfiltered_channels" "${CFGFILE}" "")"
        case "${entry}" in
        [Tt][Rr][Uu][Ee]|1)
            gcs_var "o_channel_map_mode" "manual"
            ignore_unmapped=1
            ;;
        *)
            gcs_var "o_channel_map_mode" "semi"
            ;;
        esac

        # read config file entries
        while read -r src dest
        do
            gcs_var "o_channel_map_src${mapnum}" "${src}"
            gcs_var "o_channel_map_dest${mapnum}" "${dest}"

	    read_metadata_for_channel "${src}"

            ((++mapnum))
        done < <(gcs_list_varcf2 "${CFGFILE}" "channel_map")
    fi

    # use seedmap
    if [ "${ignore_unmapped}" -eq 0 ]
    then
        while read -r src dest
        do
            # skip entries which are manually mapped
	    if [ -r "$CFGFILE" ]
	    then
		entry="$(gcs_get_varcf2 "${src}" "${CFGFILE}" "channel_map")"
		[ -z "${entry}" ] || continue
	    fi

            gcs_var "o_channel_map_src${mapnum}" "${src}"
            gcs_var "o_channel_map_dest${mapnum}" "${dest}"

	    read_metadata_for_channel "${src}"

            ((++mapnum))
        done < <(seedmap --cd11 ${GDI_SOCKET_PATH} | sort)
    fi
}



do_check() {

    gcs_read_vars

    # Standard service control/info vars
    gcs_svc_check

    # Service dependant variables
    # Most of the checks are done by the main engine

    gcs_gdi_base_dereference "sink"
    gcs_cd11mux_dereference "source"

    if [ "${new_channel_map_mode}" == "manual" ]
    then
	check_table_not_empty "channel_map" "channel_map_src" \
			"No channels have been mapped."
    fi

    case "${new_subframe_transformation}" in
    *sign*)
        [ "${new_auth_key_id}" -eq 0 ] && gcs_err "auth_key_id" "Cannot be 0 if signing is selected."
        ;;
    esac
}



# Write channel map
do_write_channel_map() {
    local -i i
    local src dest

    for (( i = 0 ; i < $new_channel_map_rows ; i++ ))
    do
        eval src="\${new_channel_map_src${i}}"
        [ -z "${src}" ] && continue

        eval dest="\${new_channel_map_dest${i}}"

        gcs_set_varcf2 "${src}" "${CFGFILE}" "${dest}" "channel_map"
    done
}


# Write the per channel meta data
do_write_channel_meta() {
    local -i i
    local src inst calib calper

    for (( i = 0 ; i < $new_channel_meta_rows ; i++ ))
    do
        eval src="\${new_channel_meta_src${i}}"
        [ -z "${src}" ] && continue

        eval inst="\${new_channel_meta_type${i}}"
        eval calib="\${new_channel_meta_calib${i}}"
        eval calper="\${new_channel_meta_calper${i}}"

        gcs_gdi_base_set_metadata "${src}" "cd11-instrument-type" "${inst}"
        gcs_gdi_base_set_metadata "${src}" "cd11-calib" "${calib}"
        gcs_gdi_base_set_metadata "${src}" "cd11-calper" "${calper}"
    done
}


# write_marker()
#  Writes a 'marker' value into the specified section. If the marker dereferences to the value of
#  'marker_none', then don't write it. Otherwise, write the dereferenced value.
write_marker() {
    local section="$1"
    local name="$2"
    local tplname="new_${section}_${name}"
    local value="${!tplname}"

    if [ "${value}" != "${marker_none}" ]
    then
        gcs_set_varcf2 "${name}" "${CFGFILE}" "${value}" "${section}"
    fi
}

do_write() {
    do_check
    if (( gcs_errors > 0 ))
    then
        return
    fi

    if gcs_truefalse "${new_delete:-false}"
    then
        gcs_svc_delete "$name" "$sel"
    fi

    # Standard service controls
    gcs_ensure_cfgfile_exists $CTLFILE
    gcs_set_varf DESC $CTLFILE "$desc"

    # Now the service type dependant variables
    gcs_ensure_cfgfile_exists "$CFGFILE"

    ## Global
    gcs_set_varcf2 "application_description"    "${CFGFILE}" "${new_desc}"                      ""
    gcs_set_varcf2 "gdi_socket"                 "${CFGFILE}" "${new_gdi_socket}"                ""
    gcs_set_varcf2 "mux_path"                   "${CFGFILE}" "${new_mux_path}"                  ""
    gcs_set_varcf2 "management_socket"          "${CFGFILE}" "/var/run/${name}.${sel}.management" ""
    gcs_set_varcf2 "management_socket_group"    "${CFGFILE}" "data"                             ""
    gcs_set_varcf2 "management_socket_mode"     "${CFGFILE}" "0660"                             ""
    gcs_set_varcf2 "data_frame_duration"        "${CFGFILE}" "${new_data_frame_duration}"       ""
    gcs_set_varcf2 "subframe_transformation"    "${CFGFILE}" "${new_subframe_transformation}"   ""
    gcs_set_varcf2 "spyrus_slot"                "${CFGFILE}" "${new_spyrus_slot}"               ""
    gcs_set_varcf2 "auth_key_id"                "${CFGFILE}" "${new_auth_key_id}"               ""
    gcs_set_varcf2 "max_clock_differential"     "${CFGFILE}" "${new_max_clock_differential}"    ""

    ## Channel map
    gcs_clear_varcf2_section "${CFGFILE}" "channel_map"

    case "${new_channel_map_mode}" in
    automatic)
        gcs_set_varcf2 "reject_unfiltered_channels" "${CFGFILE}" "" ""
        ;;
    semi)
        gcs_set_varcf2 "reject_unfiltered_channels" "${CFGFILE}" "False" ""
        do_write_channel_map
        ;;
    manual)
        gcs_set_varcf2 "reject_unfiltered_channels" "${CFGFILE}" "True" ""
        do_write_channel_map
        ;;
    esac

    do_write_channel_meta

    ## Tamper
    gcs_clear_varcf2_section "${CFGFILE}" "tamper"
    write_marker "tamper" "equipment_housing_open"
    write_marker "tamper" "digitizing_equipment_open"
    write_marker "tamper" "vault_door_opened"
    write_marker "tamper" "authentication_seal_broken"
    write_marker "tamper" "equipment_moved"
    write_marker "tamper" "future_use_a"
    write_marker "tamper" "future_use_b"
    write_marker "tamper" "future_use_c"

    # voltage digitisation
    gcs_clear_varcf2_section "${CFGFILE}" "voltage"
    write_marker "voltage" "main_power_failure"
    write_marker "voltage" "main_power_failure_threshold"
    write_marker "voltage" "main_power_failure_gpio"
    write_marker "voltage" "backup_power_unstable"
    write_marker "voltage" "backup_power_unstable_threshold"
    write_marker "voltage" "backup_power_unstable_gpio"
    write_marker "voltage" "future_use_a"
    write_marker "voltage" "future_use_a_range"
    write_marker "voltage" "future_use_b"
    write_marker "voltage" "future_use_b_range"
    write_marker "voltage" "future_use_c"
    write_marker "voltage" "future_use_c_range"

    # Update the service script
    gcs_update_svc "$name" "$sel" "$enable" "$desc" "$CFGFILE" "$CTLFILE" \
            "$SRVBASE" "$new_socket"

    gcs_svc_reload "$name" "$sel"

    gcs_gdi_base_metadata_reload
}



case "X$1" in
X--check)    do_check    ;;
X--write)    do_write    ;;
X--read)    do_read        ;;
*)        exit 1        ;;
esac

gcs_cleanup
exit 0
