#!/usr/bin/env bash

BIN=$(cd "$(dirname $0)"; pwd)
CMD_HOME=$(dirname $BIN)
CONF=$CMD_HOME/conf
STOP_TIMEOUT=10 # seconds to wait for a clean exit

source $CONF/kend.conf

if [ -z $DATA_DIR ]; then
    echo
    echo "  [ERROR] : DATA_DIR in conf/kend.conf is not defined."
    exit 1
fi

if [ ! -d $DATA_DIR ]; then
    echo
    echo "  [ERROR] : Genesis block is not initiated: [conf/kend.conf - DATA_DIR=$DATA_DIR]"
    exit 1
fi

pidfile=$DATA_DIR/kend.pid
auto_restart_daemon_pidfile=$DATA_DIR/restart_daemon_kend.pid

__pid_run() {
    unset pid
    if [ ! -f $pidfile ]; then
        return
    fi
    PID_NUM=$(cat $pidfile)
    if [[ ! -z "$PID_NUM" ]]; then
        export pid=$(ps -p $PID_NUM -o pid=)
    fi
}

__auto_restart_daemon_pid_run() {
    unset auto_restart_daemon_pid
    if [ ! -f $auto_restart_daemon_pidfile ]; then
        return
    fi
    AUTO_RESTART_DAEMON_PID_NUM=$(cat $auto_restart_daemon_pidfile)
    if [[ ! -z "$AUTO_RESTART_DAEMON_PID_NUM" ]]; then
        export auto_restart_daemon_pid=$(ps -p $AUTO_RESTART_DAEMON_PID_NUM -o pid=)
    fi
}

__kill_timeout() {
    local PIDNUM=$1
    kill $PIDNUM
    for i in `seq 0 100 $((1000 * $STOP_TIMEOUT))`; do
        if ! kill -0 $PIDNUM 2> /dev/null; then
            echo "OK"
            return
        fi
        sleep 0.1
    done
    kill -9 $PIDNUM && echo "Killed"
}

#------------------------Related Auto restart daemon functions-----------------------------
__auto_restart_daemon() {
    local backOffTime=$AUTO_RESTART_INTERVAL
    local coeff=2
    while :
    do
        sleep 1
        __pid_run
        if [ -z "$pid" ]; then
            echo "INFO[`date`] node[${PID_NUM}] is down"
            if [ -f $pidfile ]; then
                echo "INFO[`date`] remove redundant pid file"
                rm -f ${pidfile}
            fi
            echo "INFO[`date`] Sleep for backOffTime.... ${backOffTime} seconds."
            sleep $backOffTime
            echo -n "INFO[`date`] "
            start_node
            backOffTime=$(echo $backOffTime $coeff | awk '{printf "%.1f\n",$1*$2}')

            echo "INFO[`date`] backOffTime = ${backOffTime}, Restarted node pid = ${PID_NUM}"
            PID_NUM=$(cat $pidfile)
            echo ""
        fi
    done
}

start_auto_restart_daemon() {
    __auto_restart_daemon_pid_run
    if [ -z $auto_restart_daemon_pid ]; then
        __auto_restart_daemon >> ${LOG_DIR}/restart_daemon.out 2>&1 &
        disown
        AUTO_RESTART_DAEMON_PID_NUM=$!
        AUTO_RESTART_DAEMON_RETVAL=$?

        set +f
        if [ $AUTO_RESTART_DAEMON_RETVAL = 0 ]; then
            echo $AUTO_RESTART_DAEMON_PID_NUM > ${auto_restart_daemon_pidfile}
            echo "Success to start auto restart daemon."
        else
            echo "Fail to start auto restart daemon."
        fi
    fi
}

stop_auto_restart_daemon() {
    __auto_restart_daemon_pid_run
    [ -z "$auto_restart_daemon_pid" ] && echo "auto restart daemon is not running" && return
    echo -n "Shutting down auto restart daemon:  "
    __kill_timeout $(cat ${auto_restart_daemon_pidfile}) && rm -f ${auto_restart_daemon_pidfile}
}

status_auto_restart_daemon() {
    __auto_restart_daemon_pid_run
    if [ -n "$auto_restart_daemon_pid" ]; then
        echo "auto restart daemon is running."
    else
        echo "auto restart daemon is down."
    fi
}

#------------------------Related to Kaia node functions-----------------------------

__check_option() {
    if [ ! -d $LOG_DIR ]; then
        mkdir -p $LOG_DIR
    fi

    set -f
    OPTIONS=""

    if [[ ! -z $METRICS ]] && [[ $METRICS -eq 1 ]]; then
        OPTIONS="$OPTIONS --metrics"
    fi

    if [[ ! -z $PROMETHEUS ]] && [[ $PROMETHEUS -eq 1 ]]; then
        OPTIONS="$OPTIONS --prometheus"
    fi

    if [[ ! -z $DB_NO_PARALLEL_WRITE ]] && [[ $DB_NO_PARALLEL_WRITE -eq 1 ]]; then
        OPTIONS="$OPTIONS --db.no-parallel-write"
    fi

    if [[ ! -z $MULTICHANNEL ]] && [[ $MULTICHANNEL -eq 1 ]]; then
        OPTIONS="$OPTIONS --multichannel"
    fi

    if [[ ! -z $RPC_ENABLE ]] && [[ $RPC_ENABLE -eq 1 ]]; then
        OPTIONS="$OPTIONS --rpc"
        RPC_API=`echo $RPC_API | tr -d "[:space:]"`
        if [ ! -z $RPC_API ]; then
            OPTIONS="$OPTIONS --rpcapi $RPC_API"
        fi
        if [ ! -z $RPC_PORT ]; then
            OPTIONS="$OPTIONS --rpcport $RPC_PORT"
        fi
        if [ ! -z $RPC_ADDR ]; then
            OPTIONS="$OPTIONS --rpcaddr $RPC_ADDR"
        fi
        if [ ! -z $RPC_CORSDOMAIN ]; then
            OPTIONS="$OPTIONS --rpccorsdomain $RPC_CORSDOMAIN"
        fi
        if [ ! -z $RPC_VHOSTS ]; then
            OPTIONS="$OPTIONS --rpcvhosts $RPC_VHOSTS"
        fi
        if [ ! -z $RPC_CONCURRENCYLIMIT ]; then
            OPTIONS="$OPTIONS --rpc.concurrencylimit $RPC_CONCURRENCYLIMIT"
        fi
        if [ ! -z $RPC_READ_TIMEOUT ]; then
            OPTIONS="$OPTIONS --rpcreadtimeout $RPC_READ_TIMEOUT"
        fi
        if [ ! -z $RPC_WRITE_TIMEOUT ]; then
            OPTIONS="$OPTIONS --rpcwritetimeout $RPC_WRITE_TIMEOUT"
        fi
        if [ ! -z $RPC_IDLE_TIMEOUT ]; then
            OPTIONS="$OPTIONS --rpcidletimeout $RPC_IDLE_TIMEOUT"
        fi
        if [ ! -z $RPC_EXECUTION_TIMEOUT ]; then
            OPTIONS="$OPTIONS --rpcexecutiontimeout $RPC_EXECUTION_TIMEOUT"
        fi
    fi

    if [[ ! -z $WS_ENABLE ]] && [[ $WS_ENABLE -eq 1 ]]; then
        OPTIONS="$OPTIONS --ws"
        WS_API=`echo $WS_API | tr -d "[:space:]"`
        if [ ! -z $WS_API ]; then
            OPTIONS="$OPTIONS --wsapi $WS_API"
        fi
        if [ ! -z $WS_PORT ]; then
            OPTIONS="$OPTIONS --wsport $WS_PORT"
        fi
        if [ ! -z $WS_ADDR ]; then
            OPTIONS="$OPTIONS --wsaddr $WS_ADDR"
        fi
        if [ ! -z $WS_ORIGINS ]; then
            OPTIONS="$OPTIONS --wsorigins $WS_ORIGINS"
        fi
    fi

    # Mainnet network => NETWORK_ID is null && NETWORK = "mainnet"
    # Kairos  network => NETWORK_ID is null && NETWORK = "kairos"
    # Else => private network
    if [[ -z $NETWORK_ID ]]; then
        if [[ $NETWORK == "kairos" || $NETWORK == "baobab" ]]; then
            OPTIONS="$OPTIONS --kairos"
        elif [[ $NETWORK == "mainnet" || $NETWORK == "cypress" ]]; then
            OPTIONS="$OPTIONS --mainnet"
        else
            echo
            echo "[ERROR] network id is not specified and network is not available."
            echo "Available network: kairos, mainnet"
            exit 1
        fi
    else
        OPTIONS="$OPTIONS --networkid $NETWORK_ID"
        echo "[INFO] creating a private network: $NETWORK_ID"
        if [[ ! -z $NETWORK ]]; then
            echo
            echo "[WARN] ignoring the specified network: $NETWORK"
        fi
    fi

    if [ ! -z $DATA_DIR ]; then
        OPTIONS="$OPTIONS --datadir $DATA_DIR"
    fi

    if [ ! -z $PORT ]; then
        OPTIONS="$OPTIONS --port $PORT"
    fi

    if [ ! -z $SUBPORT ]; then
        OPTIONS="$OPTIONS --subport $SUBPORT"
    fi

    if [ ! -z $SERVER_TYPE ]; then
        OPTIONS="$OPTIONS --srvtype $SERVER_TYPE"
    fi

    if [ ! -z $VERBOSITY ]; then
        OPTIONS="$OPTIONS --verbosity $VERBOSITY"
    fi

    if [ ! -z $TXPOOL_EXEC_SLOTS_ALL ]; then
        OPTIONS="$OPTIONS --txpool.exec-slots.all $TXPOOL_EXEC_SLOTS_ALL"
    fi

    if [ ! -z $TXPOOL_NONEXEC_SLOTS_ALL ]; then
        OPTIONS="$OPTIONS --txpool.nonexec-slots.all $TXPOOL_NONEXEC_SLOTS_ALL"
    fi

    if [ ! -z $TXPOOL_EXEC_SLOTS_ACCOUNT ]; then
        OPTIONS="$OPTIONS --txpool.exec-slots.account $TXPOOL_EXEC_SLOTS_ACCOUNT"
    fi

    if [ ! -z $TXPOOL_NONEXEC_SLOTS_ACCOUNT ]; then
        OPTIONS="$OPTIONS --txpool.nonexec-slots.account $TXPOOL_NONEXEC_SLOTS_ACCOUNT"
    fi

    if [ ! -z $SYNCMODE ]; then
        OPTIONS="$OPTIONS --syncmode $SYNCMODE"
    fi

    if [ ! -z $MAXCONNECTIONS ]; then
        OPTIONS="$OPTIONS --maxconnections $MAXCONNECTIONS"
    fi

    if [ ! -z $LDBCACHESIZE ]; then
        OPTIONS="$OPTIONS --db.leveldb.cache-size $LDBCACHESIZE"
    fi

    if [ ! -z $PDBCACHESIZE ]; then
        OPTIONS="$OPTIONS --db.pebbledb.cache-size $PDBCACHESIZE"
    fi

    if [[ ! -z $SC_MAIN_BRIDGE ]] && [[ $SC_MAIN_BRIDGE -eq 1 ]]; then
        OPTIONS="$OPTIONS --mainbridge --mainbridgeport $SC_MAIN_BRIDGE_PORT"
        if [[ ! -z $SC_MAIN_BRIDGE_INDEXING ]] && [[ $SC_MAIN_BRIDGE_INDEXING -eq 1 ]]; then
            OPTIONS="$OPTIONS --childchainindexing"
        fi
    fi

    if [[ ! -z $NO_DISCOVER ]] && [[ $NO_DISCOVER -eq 1 ]]; then
        OPTIONS="$OPTIONS --nodiscover"
    fi

    if [[ ! -z $BOOTNODES ]] && [[ $BOOTNODES != "" ]]; then
        OPTIONS="$OPTIONS --bootnodes $BOOTNODES"
    fi

    if [[ ! -z $ADDITIONAL ]] && [[ $ADDITIONAL != "" ]]; then
        OPTIONS="$OPTIONS $ADDITIONAL"
    fi

    if [[ ! -z $AUTO_RESTART ]] && [[ $AUTO_RESTART -eq 1 ]]; then
        OPTIONS="$OPTIONS --autorestart.enable"
    fi

    if [[ ! -z $LOG_ROTATE ]] && [[ $LOG_ROTATE -eq 1 ]]; then
        OPTIONS="$OPTIONS --log.rotate"
        if [[ ! -z $LOG_MAXSIZE ]]; then
          OPTIONS="$OPTIONS --log.maxsize $LOG_MAXSIZE"
        fi
        if [[ ! -z $LOG_MAXBACKUP ]]; then
          OPTIONS="$OPTIONS --log.maxbackup $LOG_MAXBACKUP"
        fi
        if [[ ! -z $LOG_MAXAGE ]]; then
          OPTIONS="$OPTIONS --log.maxage $LOG_MAXAGE"
        fi
        if [[ ! -z $LOG_COMPRESS ]] && [[ $LOG_COMPRESS -eq 1 ]]; then
          OPTIONS="$OPTIONS --log.compress"
        fi
    fi

    if [[ ! -z $DB_TYPE ]] && [[ $DB_TYPE != "" ]]; then
        OPTIONS="$OPTIONS --dbtype $DB_TYPE"
    fi

    if [[ ! -z $DB_SINGLE ]] && [[ $DB_SINGLE -eq 1 ]]; then
        OPTIONS="$OPTIONS --db.single"
    fi

    if [[ ! -z $DB_DYNAMO_TABLENAME ]] && [[ $DB_DYNAMO_TABLENAME != "" ]]; then
        OPTIONS="$OPTIONS --db.dynamo.tablename $DB_DYNAMO_TABLENAME"
    fi

    if [[ ! -z $DB_DYNAMO_READONLY ]] && [[ $DB_DYNAMO_READONLY -eq 1 ]]; then
        OPTIONS="$OPTIONS --db.dynamo.read-only"
    fi

    if [[ ! -z $GCMODE ]] && [[ $GCMODE != "" ]]; then
        OPTIONS="$OPTIONS --gcmode $GCMODE"
    fi

    if [[ ! -z $STATE_TRIE_CACHE_LIMIT ]] && [[ $STATE_TRIE_CACHE_LIMIT != "" ]]; then
        OPTIONS="$OPTIONS --state.trie-cache-limit $STATE_TRIE_CACHE_LIMIT"
    fi

    if [[ ! -z $STATEDB_CACHE_TYPE ]] && [[ $STATEDB_CACHE_TYPE != "" ]]; then
        OPTIONS="$OPTIONS --statedb.cache.type $STATEDB_CACHE_TYPE"
        if [[ ! -z $STATEDB_REDIS_ENDPOINT ]] && [[ $STATEDB_REDIS_ENDPOINT != "" ]]; then
            OPTIONS="$OPTIONS --statedb.cache.redis.endpoints $STATEDB_REDIS_ENDPOINT"
        fi

        if [[ ! -z $STATEDB_REDIS_PUBLISH ]] && [[ $STATEDB_REDIS_PUBLISH -eq 1 ]]; then
            OPTIONS="$OPTIONS --statedb.cache.redis.publish"
        fi

        if [[ ! -z $STATEDB_REDIS_SUBSCRIBE ]] && [[ $STATEDB_REDIS_SUBSCRIBE -eq 1 ]]; then
            OPTIONS="$OPTIONS --statedb.cache.redis.subscribe"
        fi
    fi

    if [[ ! -z $VM_INTERNALTX ]] && [[ $VM_INTERNALTX -eq 1 ]]; then
        OPTIONS="$OPTIONS --vm.internaltx"
    fi

    if [[ ! -z $KES_NODETYPE_SERVICE_ENABLE ]] && [[ $KES_NODETYPE_SERVICE_ENABLE -eq 1 ]]; then
        OPTIONS="$OPTIONS --kes.nodetype.service"
    fi
}

start_node() {
    __pid_run
    [ -n "$pid" ] && echo "kend already running...[$pid]" && return

    echo -n "Starting kend: "

    __check_option

    BASEDIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
    CURRENTFILE=`basename "$0"`
    OPTIONS="$OPTIONS --autorestart.daemon.path $BASEDIR/$CURRENTFILE"

    OPTIONS="$OPTIONS --log.file ${LOG_DIR}/kend.out"
    TERM="dumb" $BIN/ken $OPTIONS >> ${LOG_DIR}/kend.out 2>&1 &
    RETVAL=$?
    PIDNUM=$!
    set +f
    if [ $RETVAL = 0 ]; then
        echo $PIDNUM > ${pidfile}
        echo "Success to start node."
    else
        echo "Fail to start node."
    fi
    return $RETVAL
}

stop_node() {
    __pid_run
    [ -z "$pid" ] && echo "kend is not running" && return
    echo -n "Shutting down kend: "
    __kill_timeout $(cat ${pidfile}) && rm -f ${pidfile}
}

status_node() {
    __pid_run
    if [ -n "$pid" ]; then
        echo "kend is running"
    else
        echo "kend is down"
    fi
}

#--------------------- Public functions --------------------------
start() {
    if [ ! -d $LOG_DIR ]; then
        mkdir -p $LOG_DIR
    fi

    if [ ! -z $AUTO_RESTART_NODE ] && [[ $AUTO_RESTART_NODE -eq 1 ]]; then
        start_auto_restart_daemon
    else
        start_node
    fi
}

start_docker() {
    echo -n "Starting kend: "
    __check_option

    echo "$BIN/ken $OPTIONS"
    $BIN/ken $OPTIONS
}

stop() {
    if [ ! -z $AUTO_RESTART_NODE ] && [[ $AUTO_RESTART_NODE -eq 1 ]]; then
        stop_auto_restart_daemon
    fi
    stop_node
}


status() {
    status_node
    if [ ! -z $AUTO_RESTART_NODE ] && [[ $AUTO_RESTART_NODE -eq 1 ]]; then
        status_auto_restart_daemon
    fi
}


restart() {
    stop
    sleep 3
    start
}

case "$1" in
    start)
        start
        ;;
    start-docker)
        start_docker
        ;;
    stop)
        stop
        ;;
    status)
        status
        ;;
    restart)
        restart
        ;;
    *)
        echo "Usages: kend {start|start-docker|stop|restart|status}"
        exit 1
        ;;
esac
exit 0
