#!/bin/bash

init () {

    # Log settings
    LOGDATEFORMAT=${LOGDATEFORMAT:="%b %e %H:%M:%S"} 
    LOG_FILE=${LOGFILE:=/tmp/log-$USER.log}
    LOG_ENABLED=${LOG_ENABLED:="yes"}

    # Get current term
    TERM=${TERM:="xterm"}

    # Colors
    RED="tput -T $TERM setaf 1"
    GREEN="tput -T $TERM setaf 2"
    YELLOW="tput -T $TERM setaf 3"
    BLUE="tput -T $TERM setaf 4"
    MAGENTA="tput -T $TERM setaf 5"
    CYAN="tput -T $TERM setaf 6"
    LIGHT_BLUE="$CYAN"
    BOLD="tput -T $TERM bold"
    DEFAULT="tput -T $TERM sgr0"
    RED_BG="tput -T $TERM setab 1"
    GREEN_BG="tput -T $TERM setab 2"
    YELLOW_BG="tput -T $TERM setab 3"
    BLUE_BG="tput -T $TERM setab 4"
    MAGENTA_BG="tput -T $TERM setab 5"
    CYAN_BG="tput -T $TERM setab 6"

    # Bug fix for Bash, parsing exclamation mark.
    set +o histexpand
}

function defined {
    [[ ${!1-X} == ${!1-Y} ]]
}

function has_value {
    if defined $1; then
        if [[ -n ${!1} ]]; then
            return 0
        fi
    fi
    return 1
}

function directory_exists {
    if [[ -d "$1" ]]; then
        return 0
    fi
    return 1
}

function file_exists {
    if [[ -f "$1" ]]; then
        return 0
    fi
    return 1
}

function tolower {
    echo "$1" | tr '[:upper:]' '[:lower:]'
}

function toupper {
    echo "$1" | tr '[:lower:]' '[:upper:]'
}

function trim {
    echo $1
}

option_enabled () {

    VAR="$1"
    VAR_VALUE=$(eval echo \$$VAR)
    if [[ "$VAR_VALUE" == "y" ]] || [[ "$VAR_VALUE" == "yes" ]]
    then
        return 0
    else
        return 1
    fi
}

log () {
    if ! option_enabled LOG_ENABLED; then
        return 1
    fi

    LOG_MESSAGE="$1" 
    DATE=`date +"$LOGDATEFORMAT"`

    LOG_STRING="$DATE ${LOG_MESSAGE:='-- empty log message, no input received'}"
    echo "$LOG_STRING" >> "$LOG_FILE"

    return 0
}

msg () {

    MESSAGE="$1"
    COLOR="$2"

    if ! has_value "MESSAGE"; then
        return 1
    fi

    ${COLOR:=$DEFAULT}
    echo "$MESSAGE" 
    $DEFAULT

    return 0
}

msg_status () {

    MESSAGE="$1"
    STATUS="$2"

    msg "$MESSAGE" && display_status "$STATUS"
}

msg_emergency () {

    MESSAGE="$1"
    STATUS="EMERGENCY"
    msg_status "$MESSAGE" "$STATUS"
}

msg_alert () {

    MESSAGE="$1"
    STATUS="ALERT"
    msg_status "$MESSAGE" "$STATUS"
}

msg_critical () {

    MESSAGE="$1"
    STATUS="CRITICAL"
    msg_status "$MESSAGE" "$STATUS"
}

msg_error () {

    MESSAGE="$1"
    STATUS="ERROR"
    msg_status "$MESSAGE" "$STATUS"
}

msg_warning () {

    MESSAGE="$1"
    STATUS="WARNING"
    msg_status "$MESSAGE" "$STATUS"
}

msg_notice () {
    MESSAGE="$1"
    STATUS="NOTICE"
    msg_status "$MESSAGE" "$STATUS"
}

msg_info () {
    MESSAGE="$1"
    STATUS="INFO"
    msg_status "$MESSAGE" "$STATUS"
}

msg_debug () {
    MESSAGE="$1"
    STATUS="DEBUG"
    msg_status "$MESSAGE" "$STATUS"
}

msg_ok () {

    MESSAGE="$1"
    STATUS="OK"
    msg_status "$MESSAGE" "$STATUS"
}

msg_not_ok () {

    MESSAGE="$1"
    STATUS="NOT_OK"
    msg_status "$MESSAGE" "$STATUS"
}

msg_fail () {

    MESSAGE="$1"
    STATUS="FAILED"
    msg_status "$MESSAGE" "$STATUS"
}

msg_success () {
    MESSAGE="$1"
    STATUS="SUCCESS"
    msg_status "$MESSAGE" "$STATUS"
}

msg_passed () {
    MESSAGE="$1"
    STATUS="PASSED"
    msg_status "$MESSAGE" "$STATUS"
}

check_status () {

    CMD="$1"
    STATUS="$2"

    if [ "$STATUS" == "0" ]
    then
        msg_ok "$CMD"
    else
        msg_fail "$CMD"
    fi
}

raw_status () {

    STATUS="$1"
    COLOR="$2"

    function position_cursor () {

        let RES_COL=`tput -T xterm cols`-12
        tput -T $TERM cuf $RES_COL
        tput -T $TERM cuu1
    }

    position_cursor
    echo -n "["
    $DEFAULT
    $BOLD
    $COLOR
    echo -n "$STATUS"
    $DEFAULT
    echo "]"
}

display_status () {


    STATUS="$1"

    case $STATUS in 

    EMERGENCY )
            STATUS="EMERGENCY"
            COLOR="$RED"
            ;;
    ALERT )
            STATUS="  ALERT  "
            COLOR="$RED"
            ;;
    CRITICAL )
            STATUS="CRITICAL "
            COLOR="$RED"
            ;;
    ERROR )
            STATUS="  ERROR  " 
            COLOR="$RED"
            ;;

    WARNING )
            STATUS=" WARNING "  
            COLOR="$YELLOW"
            ;;

    NOTICE )
            STATUS=" NOTICE  "  
            COLOR="$BLUE"
            ;;
    INFO )
            STATUS="  INFO   "  
            COLOR="$LIGHT_BLUE"
            ;;
    DEBUG )
            STATUS="  DEBUG  "
            COLOR="$DEFAULT"
            ;;    

    OK  ) 
            STATUS="   OK    "  
            COLOR="$GREEN"
            ;;
    NOT_OK)
            STATUS=" NOT OK  "
            COLOR="$RED"
            ;;

    PASSED ) 
            STATUS=" PASSED  "  
            COLOR="$GREEN"
            ;;

    SUCCESS ) 
            STATUS=" SUCCESS "  
            COLOR="$GREEN"
            ;;
    
    FAILURE | FAILED )
            STATUS=" FAILED  "  
            COLOR="$RED"
            ;;

    *)
            STATUS="UNDEFINED"
            COLOR="$YELLOW"
    esac

    raw_status "$STATUS" "$COLOR"
}

cmd () {

    COMMAND="$1"
    CMD_STR="${COMMAND:0:59}..."

    msg "Exc: $CMD_STR"

    RESULT=$(eval $COMMAND 2>&1)
    ERROR="$?"

    MSG="Cmd: $CMD_STR"
    
    tput -T $TERM cuu1

    if [ "$ERROR" == "0" ]; then
        msg_ok "$MSG"
    else
        msg_fail "$MSG"
        echo "$RESULT"
    fi

    log "$COMMAND"
    if [[ ! -z "$RESULT" ]]; then
        log "$RESULT"
    fi

    return "$ERROR"
}

#
# These functions can be used for timing how long (a) command(s) take to
# execute.
#
now () {

   echo $(date +%s)
}

elapsed () {
    
    START="$1"
    STOP="$2"

    echo $(( STOP - START ))
}

#
# Prints an error message ($2) to stderr and exits with the return code ($1).
# The message is also logged.
#
function die {
    local -r err_code="$1"
    local -r err_msg="$2"
    local -r err_caller="${3:-$(caller 0)}"

    msg_fail "ERROR: $err_msg"
    msg_fail "ERROR: At line $err_caller"
    msg_fail "ERROR: Error code = $err_code"
    exit "$err_code"
} >&2 # function writes to stderr

#
# Check if a return code ($1) indicates an error (i.e. >0) and prints an error
# message ($2) to stderr and exits with the return code ($1).
# The error is also logged.
#
# Die if error code is false.
#
function die_if_false {
    local -r err_code=$1
    local -r err_msg=$2
    local -r err_caller=$(caller 0)

    if [[ "$err_code" != "0" ]]
    then
        die $err_code "$err_msg" "$err_caller"
    fi
} >&2 # function writes to stderr

#
# Dies when error code is true
#
function die_if_true {
    local -r err_code=$1
    local -r err_msg=$2
    local -r err_caller=$(caller 0)

    if [[ "$err_code" == "0" ]]
    then
        die $err_code "$err_msg" "$err_caller"
    fi
} >&2 # function writes to stderr

#
# Replace some text inside a string.
#
function str_replace () {
    local ORIG="$1"
    local DEST="$2"
    local DATA="$3"

    echo "$DATA" | sed "s/$ORIG/$DEST/g"
}

#
# Replace string of text in file.
# Uses the ed editor to replace the string.
#
# arg1 = string to be matched
# arg2 = new string that replaces matched string
# arg3 = file to operate on.
#
function str_replace_in_file () {
    local ORIG="$1"
    local DEST="$2"
    local FILE="$3"

    has_value FILE 
    die_if_false $? "Empty argument 'file'"
    file_exists "$FILE"
    die_if_false $? "File does not exist"

    printf ",s/$ORIG/$DEST/g\nw\nQ" | ed -s "$FILE" > /dev/null 2>&1
    return "$?"
}

init


# Other stuff
check_program () {
    program=$1
    which $program 1>/dev/null || {
        die 127 "Command '$program' not found!" "$(caller 0)"
    }
}

cmd_or_die () {
    cmd "$1" || exit 1
}
