#!/bin/sh

set -u

source /lib/functions/navico_env.sh
source /lib/functions/upd_datestamp.sh
source /usr/libexec/nos-hal/storage.sh
navico_env_init

show_usage() {
    cat >&2 <<EO_USAGE
Usage: ${0##*/} FUNCTION [ARGS]

	FUNCTION	ARGS
	burnin-status	{in-progress|completed|overtime|error|exit}
	hdmi-output	{enable|disabled-hdcp}
	ethernet	{get-config|set-config <IP>|validate [<IP>]}
	sonar		{powerdown|stop|restart <args>|start <args>|is-running}
	sxedl		{start|stop|restart}
	prepare-update	{<path/to/updater>}
	codename
	factory-datestamp
	dump-syslog     <output-file>
	dump-dmesg      <output-file>
	ftp-download    {serverip local-file remote-file}
	eth-dhcp-mode   {client|server}
	get-storage-type
	get-storage-details {emmc|nand}
	language-pack   {reset <lang>}
	nos-feature     {remove <filename>}
EO_USAGE
}

show_ethernet_usage() {
    cat >&2 <<EO_USAGE
Usage: ${0##*/} FUNCTION [ARGS]

        FUNCTION	ARGS
        get-config
        set-config	{<IP>}
        validate	{<IP>}

EO_USAGE
}

IP_INTERFACE_ALIAS="eth0:lwe"
IP_INTERFACE="eth0"

ip_in_range() {
    printf '%s ' "$@" | awk '
        function ip2int(ip) {
            res=0;
            n=split(ip, a, ".");
            if (n != 4) {
                exit 1;
            }
            for (n=1 ; n<=4; n++) {
                v=a[n];

                if (v !~ /^[0-9]{1,3}$/ || v > 255) {
                    exit 1;
                }

                res=256*res + v;
            }

            return res;
        }

        {
            lo=ip2int($1);
            hi=ip2int($2);
            ip=ip2int($3);

            if ( ip < lo || ip > hi ) exit 2;
        }'
}

# Consider to move this function body to navico_env_init
navico_familyseg_init() {
    case $CODENAME in
        lisa|thor)
            FAMILYSEG=pro
            ;;
        *)
            FAMILYSEG=rec
            ;;
    esac
}

ethernet_list_subnets() {
    [ -n "${FAMILYSEG:-}" ] || navico_familyseg_init

    if [ "$FAMILYSEG" = 'pro' ]; then
        printf '\nPlease select IP from ranges available for current platform:\n'
        printf '\t10.0.0.1    - 10.255.255.253  (10/8 prefix,       /8 mask)\n'
        printf '\t172.16.0.1  - 172.21.255.254  (172.16/12 prefix,  /16 mask)\n'
        printf '\t172.23.0.1  - 172.31.255.254  (172.16/12 prefix,  /16 mask)\n'
        printf '\t172.24.0.1  - 172.31.255.254  (172.24/13 prefix,  /13 mask)\n'
        printf '\t192.168.0.1 - 192.168.255.254 (192.168/16 prefix, /24 mask)\n\n'
    else
        printf '\nPlease select IP from range available for current platform:\n'
        printf '\t172.16.0.1  - 172.21.255.254  (172.16/12 prefix,  /16 mask)\n'
        printf '\t172.24.0.1  - 172.31.255.254  (172.24/13 prefix,  /13 mask)\n\n'
    fi
}

ethernet_validate() {
    [ -n "${FAMILYSEG:-}" ] || navico_familyseg_init
    IP_ADDR=${1:-}
    IP_MASK=''

    if [ -z "$IP_ADDR" ]; then
        ethernet_list_subnets
        return 3
    fi

    if ! ip_in_range 0.0.0.0 255.255.255.255 "$IP_ADDR"; then
        printf 'Invalid IP address specified: "%s"\n' "$IP_ADDR" 1>&2
        return 6

    elif ip_in_range 172.22.0.0 172.22.255.255 "$IP_ADDR"; then
        printf 'Subnet 172.22.0.0/16 reserved for weather modules.\n' 1>&2
        return 7

    elif ip_in_range 172.23.0.0 172.23.255.255 "$IP_ADDR"; then
        printf 'Subnet 172.23.0.0/16 reserved for IRIS cameras.\n' 1>&2
        return 8

    elif ip_in_range 172.16.0.0 172.31.255.255 "$IP_ADDR"; then
        local prefix_16mask=$(printf '%s' "$IP_ADDR" | awk -F'.' '{print $1"."$2}')
        if ip_in_range ${prefix_16mask}.0.1 ${prefix_16mask}.255.254 "$IP_ADDR"; then
            IP_MASK='/16'
            echo "${IP_ADDR}${IP_MASK} - OK"
            return 0
        else
            printf 'Invalid IP address for %s.0.0/16 subnet, should be in range: %s.0.1 - %s.255.254\n' "$prefix_16mask" "$prefix_16mask" "$prefix_16mask" 1>&2
            return 4
        fi

    elif [ "$FAMILYSEG" = 'rec' ]; then
        printf 'IP specified does not fit any of subnets available for this device\n' 1>&2
        return 5
        
    elif ip_in_range 192.168.0.0 192.168.255.255 "$IP_ADDR"; then
        local prefix_24mask=$(printf '%s' "$IP_ADDR" | awk -F'.' '{print $1"."$2"."$3}')
        if ip_in_range ${prefix_24mask}.1 ${prefix_24mask}.254 "$IP_ADDR"; then
            IP_MASK='/24'
            echo "${IP_ADDR}${IP_MASK} - OK"
            return 0
        else
            printf 'Invalid IP address for %s.0/24 subnet, should be in range: %s.1 - %s.254\n' "$prefix_24mask" "$prefix_24mask" "$prefix_24mask" 1>&2
            return 4
        fi

    elif ip_in_range 10.0.0.1 10.255.255.253 "$IP_ADDR"; then
        IP_MASK='/8'
        echo "${IP_ADDR}${IP_MASK} - OK"
        return 0

    else
        printf 'IP specified does not fit any of subnets available for this device\n' 1>&2
        return 5
    fi
}

ethernet_set() {
    if ethernet_validate $@ > /dev/null; then
        sudo -n /sbin/ip address flush label ${IP_INTERFACE_ALIAS} dev ${IP_INTERFACE}
        sudo -n /sbin/ip address add ${IP_ADDR}${IP_MASK} label ${IP_INTERFACE_ALIAS} dev ${IP_INTERFACE}
        return
    else
        printf 'Can not set invalid IP: "%s"\n' "$IP_ADDR" 1>&2
        return 2
    fi
}

ethernet_get() {
    local ip_addr=$(/sbin/ip address show label ${IP_INTERFACE_ALIAS} | awk -F" " '/inet/ {print substr($2, 0, index($2, "/")-1)}')
    if [ -n "${ip_addr}" ]; then
        echo "${ip_addr}"
        return
    fi
    return 1
}

ethernet () {
    local command=${1:-}

    navico_familyseg_init
    case $command in
    validate)
        shift && ethernet_validate "$@"
        ;;
    set-config)
        shift && ethernet_set "$@"
        ;;
    get-config)
        ethernet_get
        ;;
    *)
        printf 'Unsupported argument "%s"\n' "$command"
        show_ethernet_usage
        return 1
        ;;
    esac
}

burnin_status_hatchetfish() {
    local state="$1"
    local led_cmd=34
    local cpu=$((SUBFAMILYID - 1))
    local green_led=1
    local red_led=2
    local both_leds=3
    local colour2=0
    local priority duty_ms colour1

    case "$state" in
    in-progress)
        priority=11
        duty_ms=150
        colour1="$both_leds"
        ;;
    completed)
        priority=10
        duty_ms=150
        colour1="$green_led"
        ;;
    overtime)
        priority=12
        duty_ms=50
        colour1="$both_led"
        ;;
    error)
        priority=14
        duty_ms=50
        colour1="$red_led"
        ;;
    exit)
        priority=0
        duty_ms=0
        colour1=0
        ;;
    *)
        printf 'Unsupported burnin-status "%s"\n' "$status" 1>&2
        return 1
        ;;
    esac

    duty_ms=$((duty_ms / 10)) # mcu-util wants tens of ms
    echo "t $led_cmd $cpu $priority $colour1 $duty_ms $colour2 $duty_ms" | mcu-util
}

burnin_status_komodo() {
    local state="$1"
    local red_led_path=/sys/class/leds/status-red
    local green_led_path=/sys/class/leds/status-green

    set_timer_trigger() {
        local led_path="$1"
        local duty_ms="$2"
        echo timer > "${led_path}/trigger"
        echo "$duty_ms" > "${led_path}/delay_on"
        echo "$duty_ms" > "${led_path}/delay_off"
    }

    local led_path
    case "$state" in
    in-progress)
        for led_path in "$red_led_path" "$green_led_path"; do
            set_timer_trigger "$led_path" 150
        done
        ;;
    completed)
        echo none > "${red_led_path}/trigger"
        echo 0 > "${red_led_path}/brightness"
        set_timer_trigger "$green_led_path" 150
        ;;
    overtime)
        for led_path in "$red_led_path" "$green_led_path"; do
            set_timer_trigger "$led_path" 50
        done
        ;;
    error)
        set_timer_trigger "$red_led_path" 50
        echo none > "${green_led_path}/trigger"
        echo 0 > "${green_led_path}/brightness"
        ;;
    exit)
        for led_path in "$red_led_path" "$green_led_path"; do
            echo none > "${led_path}/trigger"
            echo 0 > "${led_path}/brightness"
        done
        ;;
    *)
        printf 'Unsupported burnin-status "%s"\n' "$status" 1>&2
        return 1
        ;;
    esac
}

burnin_status() {
    local args="$@"

    case "$FAMILYID" in
    Hatchetfish)
        burnin_status_hatchetfish "$args"
        ;;
    Komodo)
        burnin_status_komodo "$args"
        ;;
    *)
        ;;
    esac
}

hdmi_output() {
    local state=${1:-}
    local hdmi_disable=/run/shm/hdmi-output-disable

    case $state in
    enable)
        if [ -e "$hdmi_disable" ]; then
            rm "$hdmi_disable"
            /lib/udev/udev_hdmi_action.sh --probe
        fi
        ;;
    disabled-*)
        echo "$state" > "$hdmi_disable"
        /lib/udev/udev_hdmi_action.sh --probe
        ;;
    *)
        printf 'Unsupported argument "%s"\n' "$state"
        return 1
        ;;
    esac
}

ftp_download() {
    local server_ip=${1:-}
    local local_file=${2:-}
    local remote_file=${3:-}

    if [ -n "$local_file" -a -n "$remote_file" ]; then
        ftpget "$server_ip" -u ftp "$local_file" "$remote_file"
        return $?
    fi
    return 1
}

eth_dhcp_mode() {
    local dhcp_mode=${1:-}
    local dhcp_mode_file="/home/nos-hal/dhcp-mode"

    case $dhcp_mode in
    client|server)
        sudo -n /usr/libexec/nos-hal/set-dhcp-mode "$dhcp_mode"
        ;;
    "")
        if [ -r "$dhcp_mode_file" ]; then
            cat "$dhcp_mode_file"
        else
            case "$FAMILYID" in
            KomodoHub)
                echo "server"
                ;;
            *)
                echo "client"
                ;;
            esac
        fi
        ;;
    *)
        printf 'Unsupported argument "%s"\n' "$dhcp_mode"
        return 1;
        ;;
    esac
}

ALL_ARGS="$@"

case ${1:-} in
    hdmi-output)
        shift && hdmi_output "$@"
        ;;
    burnin-status)
        shift && burnin_status "$@"
        ;;
    ethernet)
        shift && ethernet "$@"
        ;;
    sonar)
        shift && sudo -n /usr/libexec/nos-hal/sonar "$@"
        ;;
    sxedl)
        shift && sudo -n /etc/rc.d/rc.sxedl "$@"
        ;;
    prepare-update)
        shift && sudo -n /usr/libexec/nos-hal/prepare-update "$@"
        ;;
    codename)
        echo "${CODENAME:-}"
        ;;
    factory-datestamp)
        get_factory_datestamp
        ;;
    dump-syslog)
        shift && { logread || cat /var/log/messages || cat /tmp/messages; } 2>/dev/null | tail -c 32768 > "$1"
        ;;
    ftp-download)
        shift && ftp_download "$@"
        ;;
    dump-dmesg)
        shift && /bin/dmesg > "$1"
        ;;
    eth-dhcp-mode)
        shift && eth_dhcp_mode "$@"
        ;;
    get-storage-type)
        shift && get_storage_type
        ;;
    get-storage-details)
        shift && get_storage_details "$@"
        ;;
    language-pack)
        shift && sudo -n /usr/libexec/nos-hal/language-pack "$@"
        ;;
    nos-feature)
        shift && sudo -n /usr/libexec/nos-hal/nos-feature "$@"
        ;;
    ""|-h|--help)
        show_usage
        exit 0
        ;;
    *)
        show_usage
        false
        ;;
esac

RESULT=$?
PARENT=$(tr '\0' '\t' </proc/$PPID/cmdline | cut -f1)
logger -t "${0##*/}" -p info -- "$PARENT[$PPID] $ALL_ARGS -> $RESULT"

exit $RESULT
