#!/bin/bash
#

ras="./ras"
rand="./rasrand"

FQDN=`uname -n`
HOSTNAME=${FQDN%%.*}
IP=`ifconfig | grep "inet addr" | head -1`
IP=${IP%%  Bcast*}
IP=${IP#*:}

usage()
{
    echo "$(basename $0) [options]"
    echo "  --ttl sec            time to live"
    echo "  --ttl-give sec       keepalive is sent this many seconds before ttl"
    echo "  --ept-type           terminal | gateway | mcu"
    echo "  --loaded-odds n      0 no load, or 1/n chance of being loaded"
    echo "  --loaded-recovery n  0 no recovery, or 1/n chance of recovery"
    echo "  --crash-odds n       0 no crash, or 1/n chance of crash"
    echo "  --unreg-odds n       0 no unreg, or 1/n chance of unreg"
    echo "  --recovery n         0 no recovery from crash or unreg, or 1/n"
    echo "  --recovery-time sec  time before trying to recover from crash or unreg"
    echo "  --address ip:port    address to run endpoint"
    echo "  --ras-address        address to advertise to gatekeeper"
    echo "  --phone string       phone number"
    echo "  --user               user"
    echo "  --debug"
}

TTL=60
TTL_GIVE=15

EPT_TYPE=terminal

LOADED_ODDS=0
LOADED_RECOVERY_ODDS=0

CRASH_ODDS=0
UNREG_ODDS=0

RECOVERY_TIME=0
RECOVERY_ODDS=0

USER=""
PHONE=""

ADDRESS=""
RAS_ADDR=$IP:17190

while [ $# -gt 0 ]; do
    case $1 in
        --ttl)          if [ $# -gt 1 ]; then shift; TTL=$1; fi ;;
        --ttl-give)     if [ $# -gt 1 ]; then shift; TTL_GIVE=$1; fi ;;
        --ept-type)     if [ $# -gt 1 ]; then shift; EPT_TYPE=$1; fi ;;
        --loaded-odds)  if [ $# -gt 1 ]; then shift; LOADED_ODDS=$1; fi ;;
        --loaded-recovery) if [ $# -gt 1 ]; then shift; LOADED_RECOVERY_ODDS=$1; fi ;;
        --crash-odds)   if [ $# -gt 1 ]; then shift; CRASH_ODDS=$1; fi ;;
        --unreg-odds)   if [ $# -gt 1 ]; then shift; UNREG_ODDS=$1; fi;;
        --recovery)     if [ $# -gt 1 ]; then shift; RECOVERY_ODDS=$1; fi ;;
        --recovery-time) if [ $# -gt 1 ]; then shift; RECOVERY_TIME=$1; fi ;;
        --address)      if [ $# -gt 1 ]; then shift; ADDRESS="--address $1"; fi ;;

        --ras-address)  if [ $# -gt 1 ]; then shift; RAS_ADDR=$1; fi ;;
        --phone)        if [ $# -gt 1 ]; then shift; PHONE="--phone $1"; fi ;;
        --user)         if [ $# -gt 1 ]; then shift; USER="--user $1"; fi ;;
        --debug)        DBG="--debug";;
        --verbose)      DBG="--debug"; set -x ;;
        *|-?|--help|-h) usage; exit 1 ;
    esac
    shift
done

RAS_IP=${RAS_ADDR%:*}
RAS_PORT=${RAS_ADDR#*:}

(( CALL_SIG_PORT = RAS_PORT + 10 ))
CALL_SIG_ADDR=$RAS_IP:$CALL_SIG_PORT


[ "$DBG" != "" ] && echo "TTL                       $TTL"
[ "$DBG" != "" ] && echo "TTL_GIVE                  $TTL_GIVE"
[ "$DBG" != "" ] && echo "EPT_TYPE                  $EPT_TYPE"
[ "$DBG" != "" ] && echo "LOADED_ODDS               $LOADED_ODDS"
[ "$DBG" != "" ] && echo "LOADED_RECOVERY_ODDS      $LOADED_RECOVERY_ODDS"
[ "$DBG" != "" ] && echo "CRASH_ODDS                $CRASH_ODDS"
[ "$DBG" != "" ] && echo "UNREG_ODDS                $UNREG_ODDS"
[ "$DBG" != "" ] && echo "RECOVERY_TIME             $RECOVERY_TIME"
[ "$DBG" != "" ] && echo "RECOVERY_ODDS             $RECOVERY_ODDS"
[ "$DBG" != "" ] && echo "USER                      \"$USER\""
[ "$DBG" != "" ] && echo "PHONE                     \"$PHONE\""
[ "$DBG" != "" ] && echo "ADDRESS                   \"$ADDRESS\""
[ "$DBG" != "" ] && echo "RAS_ADDR                  $RAS_ADDR"
[ "$DBG" != "" ] && echo "CALL_SIG_ADDR             $CALL_SIG_ADDR"


# usage: do_sleep seconds
#
do_sleep()
{
    local time=$1
    if [ x$time = x ]; then
        echo "Error: do_sleep seconds"
        echo "$*"
        return
    fi
    
    while [ $time -gt 0 ]; do
        sleep 1
        echo -n "."
        (( --time ))
    done
}

# usage: inc_seq
#
inc_seq()
{
    local sn=0
    eval sn=$SEQ
    
    (( ++sn ))
    if [ $sn -eq 65534 ]; then sn=1; fi
    
    eval SEQ=$sn
}

# usage: discovery seq
#
discovery()
{
    if [ $# -ne 1 ]; then
        echo "Error: discovery seq"
        return 1;
    fi
    
    local rc=0
    local sn=$1
    
    local gcf=$($ras    --grq \
                        $ADDRESS \
	                --seq $sn \
	                --ras-address $RAS_ADDR \
	                --$EPT_TYPE \
	                --retry-count 2 \
	                --retry-delay 5 \
                        --multicast $DBG)
    rc=$?
        
    if [ $rc -eq 0 ]; then
        echo "$gcf"
    fi
    
    return $rc
}

# usage: register seq gk_ras_addr [gk_id]
#
register()
{
    if [ $# -lt 2 -o $# -gt 3 ]; then
        echo "Error: register gk_ras_addr [gk_id]"
        return 1;
    fi

    local gk_id=
    if [ $# -eq 3 ]; then
        gk_id="--gk-id $3"
    fi
    
    local rc=0
    local sn=$1
    local gk_ras_addr=$2
    
    local rcf=$($ras    $gk_ras_addr \
                        --rrq \
                        $ADDRESS \
                        --sequence-number $sn \
                        --discovery-complete \
                        --call-sig-address $CALL_SIG_ADDR \
	                --ras-address $RAS_ADDR \
                        --$EPT_TYPE \
                        $PHONE \
                        $USER \
                        $gk_id \
                        --time-to-live $TTL \
                        --retry-count 2 \
                        --retry-delay 3 $DBG)
    rc=$?
    
    if [ $rc -eq 0 ]; then
        echo "$rcf"
    fi
    
    return $rc
}


# usage: ka_register seq endpoint_id gk_ras_addr [gk_id]
#
ka_register()
{
    if [ $# -lt 3 -o $# -gt 4 ]; then
        echo "Error: ka_register seq endpoint_id gk_ras_addr [gk_id]"
        return 1;
    fi

    local gk_id=
    if [ $# -eq 4 ]; then
        gk_id="--gk-id $4"
    fi
    
    local rc=0
    local sn=$1
    local ept_id=$2
    local gk_ras_addr=$3

    rcf=$($ras $gk_ras_addr \
                --rrq \
                $ADDRESS \
                --sequence-number $sn \
                --discovery-complete \
                --call-sig-address $CALL_SIG_ADDR \
	        --ras-address $RAS_ADDR \
                --$EPT_TYPE \
                $PHONE \
                $USER \
                $gk_id \
                --time-to-live $TTL \
                --keepalive \
                --endpoint-id $ept_id \
                --retry-count 2 \
                --retry-delay 3 $debug)
    rc=$?
    
    if [ $rc -eq 0 ]; then
        echo "$rcf"
    fi
    
    return $rc
}


# usage: unregister seq endpoint_id gk_ras_addr
#
unregister()
{
    if [ $# -ne 3 ]; then
        return 1;
    fi
    
    local rc=0
    local sn=$1
    local ept_it=$2
    local gk_ras_addr=$3
    
    $ras $gk_ras_addr \
            --urq \
            --sequence-number $sn \
            $ADDRESS \
            --ras-address $RAS_ADDR \
            --call-sig-address $CALL_SIG_ADDR \
            --endpoint-id $ept_id \
            --retry-count 1 \
            --retry-delay 3 $DBG
    rc=$?

    return $rc    
}


TIME_TO_LIVE="Time To Live"

# usage: do_keepalive ttl endpoint_id gk_ras_addr [gk_id]
#
do_keepalive()
{
    if [ $# -lt 3 -o $# -gt 4 ]; then
        echo "Error: keepalive ttl ept_id gk_ras_addr [gk_id]"
        return 1
    fi
    
    local rc=0
    local r=0

    local ttl=$1
    local ept_id=$2
    local gk_ras_addr=$3
    local gk_id=$4

    local wait=45
    local crash=0
    local goto_loaded=0
    local goto_unloaded=0
    local loaded=0

    
    while true ; do

        eval r=$($rand)
        
        if [ "$LOADED_ODDS" != "" -a $LOADED_ODDS -ne 0 ]; then
            if [ $loaded -eq 0 ]; then

                (( goto_loaded = !(r % $LOADED_ODDS) ))

                if [ $goto_loaded -eq 1 ]; then
                    loaded=1
                    echo "Changed to loaded condition"
                fi

            else

                eval r=$($rand)
                
                if [ "$LOADED_RECOVERY_ODDS" != "" -a $LOADED_RECOVERY_ODDS -ne 0 ]; then
                
                    (( goto_unloaded = !(r % LOADED_RECOVERY_ODDS) ))

                    if [ $goto_unloaded -eq 1 ]; then
                        loaded=0
                        echo "Recovered from load"
                    else
                        echo "Still under load"
                    fi
                fi
            fi
        fi

        if [ "$CRASH_ODDS" != "" -a $CRASH_ODDS -ne 0 ]; then
        
            eval r=$($rand)
            
            (( crash = !(r % CRASH_ODDS) ))

            if [ $crash -eq 1 ]; then
                echo "Simulating a crash"
                
                eval "((++TOTAL_RAI))"
                
                return 0
            fi
        fi

        if [ "$UNREG_ODDS" != "" -a $UNREG_ODDS -ne 0 ]; then
        
            eval r=$($rand)
            
            (( unreg = !(r % UNREG_ODDS) ))

            if [ $unreg -eq 1 ]; then
                echo "Going down gracefully"

                eval "((++TOTAL_URQ))"
                
                inc_seq 
                
                unregister $SEQ $ept_id $gk_ras_addr
                rc=$?
    
                if [ $rc -ne 0 ]; then
                    echo "Unregistration failed: $rc"
                    exit $rc
                fi

                return 0
            fi
        fi

        if [ $ttl -gt $TTL_GIVE ]; then
            (( wait = ttl - TTL_GIVE ))
        fi

        echo -n "Next keepalive in $wait seconds "

        do_sleep $wait

        inc_seq
        eval local seq=$SEQ

        eval "(( ++TOTAL_KA_RRQ ))"
        
        ka_rcf=$(ka_register $seq $ept_id $gk_ras_addr $gk_id)
        rc=$?

        if [ $rc -ne 0 ]; then
            echo -e "\nKeep alive registration failed: $rc"
            echo -e "This is expected if program is terminating via signal."
            exit $rc
        fi

        ttl=$(echo "$ka_rcf" | grep "$TIME_TO_LIVE" | tail -1)
        ttl=${ttl#*: *}

        echo " keepalive done. New time to live $ttl"

    done
}

RAS_ADDRESS="RAS Address"
GATEKEEPER_ID="Gatekeeper Id"
ENDPOINT_ID="Endpoint Id"

TOTAL_GRQ=0
TOTAL_RRQ=0
TOTAL_KA_RRQ=0
TOTAL_URQ=0
TOTAL_CRASH=0
TOTAL_RAI=0

start_time=$(date +%s)

go_down()
{
    echo -e "\nGoing down"
    eval "((++TOTAL_URQ))"
    inc_seq 
    eval unregister $SEQ $endpoint_id $gk_ras_addr
}

report()
{    
    go_down
    
    end_time=$(date +%s)
    (( total_time = end_time - start_time ))

    local   total_msg=0
    local   mps=0
    local   mps_remain=0

    eval "(( total_msg = TOTAL_GRQ + TOTAL_RRQ + TOTAL_KA_RRQ + TOTAL_URQ + TOTAL_RAI ))"
    eval "(( mps = total_msg / total_time ))"
    eval "(( mps_remain = ( ( total_msg % total_time ) * 10 ) / total_time ))"

    echo -e  "\nTotal Time:      $total_time"       >&2
    eval echo '"GRQ Total:       $TOTAL_GRQ"'       >&2
    eval echo '"RRQ Total:       $TOTAL_RRQ"'       >&2
    eval echo '"KA RRQ Total:    $TOTAL_KA_RRQ"'    >&2
    eval echo '"URQ Total:       $TOTAL_URQ"'       >&2
    eval echo '"RAI Total:       $TOTAL_RAI"'       >&2
    eval echo '"Total Message:   $total_msg"'       >&2
    eval echo '"Message per sec: $mps.$mps_remain"' >&2
}


trap report EXIT
trap go_down SIGINT

done=false

while [ "$done" != "true" ]; do

    inc_seq
    
    (( ++TOTAL_GRQ ))

    gcf=$(discovery $SEQ)
    rc=$?

    if [ $rc -ne 0 ]; then
        echo "Discovery failed: $rc"
        exit $rc
    fi

    gk_ras_addr=$(echo "$gcf" | grep "$RAS_ADDRESS" | tail -1)
    gk_ras_addr=${gk_ras_addr#*: *}

    gk_id=$(echo "$gcf" | grep "$GATEKEEPER_ID" | tail -1)
    gk_id=${gk_id#*: *}

    echo "Discovered gatekeeper: $gk_id at $gk_ras_addr"

    inc_seq
    
    (( ++TOTAL_RRQ ))
    
    rcf=$(register $SEQ $gk_ras_addr $gk_id)
    rc=$?

    if [ $rc -ne 0 ]; then
        echo "Registration failed: $rc"
        exit $rc
    fi
    
    ttl=$(echo "$rcf" | grep "$TIME_TO_LIVE" | tail -1)
    ttl=${ttl#*: *}
    
    endpoint_id=$(echo "$rcf" | grep "$ENDPOINT_ID" | tail -1)
    endpoint_id=${endpoint_id#*: *}
        
    echo "Registered as endpoint: $endpoint_id, time to live: $ttl"

    do_keepalive $ttl $endpoint_id $gk_ras_addr $gk_id
    rc=$?

    if [ $rc -ne 0 ]; then
        echo "Keep Alive registration failed: $rc"
        exit $rc
    fi

    if [ "$RECOVERY_ODDS" != "" -a $RECOVERY_ODDS -ne 0 ];  then
        recovery=0

        echo -n "Trying to recover "

        while [ $recovery -eq 0 ]; do

            do_sleep $RECOVERY_TIME

            r=0
            eval r=$($rand)

            (( recovery = !(r % RECOVERY_ODDS) ))

            if [ $recovery -ne 0 ]; then
                echo " recovered"
            fi
        done
    else
        done="true"
    fi
done
        
    

