# This bash library contains the functions that allow osbash to produce
# Windows batch files.

: ${WBATCH_DIR:="$TOP_DIR/wbatch"}

# By default, Windows batch file templates are in the same directory as this
# file
: ${WBATCH_TEMPLATE_DIR:=$(dirname "$BASH_SOURCE")}

# wbatch cannot use ssh for talking to the VM; install VirtualBox guest
# additions
VM_ACCESS=vbadd

# With VirtualBox 5.1.6, console type "headless" often gives no access to the
# VM console which on Windows is the main method for interacting with the
# cluster. Use "separate" which works at least on 5.0.26 and 5.1.6.
VM_UI=separate

#-------------------------------------------------------------------------------
# Helper functions
#-------------------------------------------------------------------------------

# See functions.host for definition and explanation of exec_cmd
WBATCH=exec_cmd

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

function wbatch_reset {
    clean_dir "$WBATCH_DIR"
    cp -v "$WBATCH_TEMPLATE_DIR/config_bat" "$WBATCH_DIR/config.bat"
}

function wbatch_new_file {
    local file_name=$1
    mkdir -p "$WBATCH_DIR"
    WBATCH_OUT="$WBATCH_DIR/$file_name"
    echo -n > "$WBATCH_OUT"
}

function wbatch_close_file {
    unset WBATCH_OUT
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

function wbatch_write_line {
    if [ -n "${WBATCH_OUT:-}" ]; then
        # Don't expand backslash escapes except for ending the line with CRLF
        #
        # Note: Windows batch scripts with LF may seem to work, but (for
        #       instance) jump labels don't work properly
        echo -n "$@" >> "$WBATCH_OUT"
        echo -e "\r" >> "$WBATCH_OUT"
    fi
}

function wbatch_write_stdin {
    local line=""

    # Set IFS to preserve leading whitespace
    while IFS= read -r line; do
        wbatch_write_line "$line"
    done
}

function wbatch_echo {
    wbatch_write_line "ECHO %time% $@"
}

#-------------------------------------------------------------------------------
# Batch function calls
#-------------------------------------------------------------------------------

function wbatch_abort_if_vm_exists {
    local vm_name=$1
    wbatch_write_line "CALL :vm_exists $vm_name"
}

function wbatch_wait_poweroff {
    local vm_name=$1
    cat << WBAT | wbatch_write_stdin
ECHO %time% Waiting for VM $vm_name to power off.
CALL :wait_poweroff $vm_name
ECHO %time% VM $vm_name powered off.
WBAT
}

function wbatch_wait_auto {
    cat << WBAT | wbatch_write_stdin
ECHO %time% Waiting for autostart files to execute.
CALL :wait_auto
ECHO %time% All autostart files executed.
WBAT
}

#-------------------------------------------------------------------------------
# Batch commands
#-------------------------------------------------------------------------------

function wbatch_delete_disk {
    local disk=$(basename "$1")
    wbatch_write_line "IF EXIST %IMGDIR%\\$disk DEL %IMGDIR%\\$disk"
}

function wbatch_rename_disk {
    local src=$(basename "$1")
    local target=$(basename "$2")

    wbatch_write_line "MOVE /y %IMGDIR%\\$src %IMGDIR%\\$target"
}

function wbatch_cp_auto {
    local src=$(wbatch_path_to_windows "$1")
    local target=$(basename "$2")
    src=${src//\//\\}
    wbatch_write_line "COPY %TOPDIR%\\$src %AUTODIR%\\$target"
}

function wbatch_sleep {
    local sec=$1
    wbatch_write_line "TIMEOUT /T $sec /NOBREAK"
}

#-------------------------------------------------------------------------------
# Templated parts
#-------------------------------------------------------------------------------

# Note: BSD and GNU sed behavior is different. Don't try anything fancy
#       like inserting \r or in-place editing (-i).

function wbatch_file_header {
    local product=$1

    sed -e "
        s,%PRODUCT%,$product,g;
        " "$WBATCH_TEMPLATE_DIR/template-file_header_bat" | wbatch_write_stdin
}

function wbatch_end_file {
    cat "$WBATCH_TEMPLATE_DIR/template-end_file_bat" | wbatch_write_stdin
    wbatch_close_file
}

function wbatch_elevate_privileges {
    cat "$WBATCH_TEMPLATE_DIR/template-elevate_privs_bat" | wbatch_write_stdin
}

function wbatch_find_vbm {
    cat "$WBATCH_TEMPLATE_DIR/template-find_vbm_bat" | wbatch_write_stdin
}

function wbatch_mkdirs {
    local autodir=$(wbatch_path_to_windows "$AUTOSTART_DIR")
    local imgdir=$(wbatch_path_to_windows "$IMG_DIR")
    local logdir=$(wbatch_path_to_windows "$LOG_DIR")
    local statusdir=$(wbatch_path_to_windows "$STATUS_DIR")

    autodir=$(wbatch_escape_backslash "$autodir")
    imgdir=$(wbatch_escape_backslash "$imgdir")
    logdir=$(wbatch_escape_backslash "$logdir")
    statusdir=$(wbatch_escape_backslash "$statusdir")

    sed -e "
        s,%P_AUTODIR%,$autodir,g;
        s,%P_IMGDIR%,$imgdir,g;
        s,%P_LOGDIR%,$logdir,g;
        s,%P_STATUSDIR%,$statusdir,g;
        " "$WBATCH_TEMPLATE_DIR/template-mkdirs_bat" | wbatch_write_stdin
}

function wbatch_create_hostnet {
    wbatch_new_file "create_hostnet.bat"
    wbatch_file_header "host-only networks"
    cat "$WBATCH_TEMPLATE_DIR/template-begin_hostnet_bat" | wbatch_write_stdin
    # Creating networks requires elevated privileges
    wbatch_elevate_privileges
    wbatch_find_vbm

    local index
    local if_name
    local win_adapter
    for index in "${!NET_NAME[@]}"; do
        if_name=${NET_IFNAME[index]}
        # The host-side interface is the default gateway of the network
        if_ip=${NET_GW[index]}
        # Translate if_name to Windows-type interface name
        win_adapter=$if_name
        sed -e "
            s,%IFNAME%,${win_adapter},g;
            s,%IFIP%,${if_ip},g;
            " "$WBATCH_TEMPLATE_DIR/template-create_hostnet_bat" | wbatch_write_stdin
        echo | wbatch_write_stdin
    done

    wbatch_end_file
}

function wbatch_begin_base {
    local iso_name=$(get_iso_name)

    if [ -z "$iso_name" ]; then
        echo >&2 "Windows batch file needs install ISO URL (ISO_URL)."
        exit 1
    fi

    wbatch_new_file "create_base.bat"
    wbatch_file_header "base disk"
    wbatch_find_vbm
    wbatch_mkdirs

    sed -e "
        s,%INSTALLFILE%,$iso_name,g;
        s,%ISOURL%,$ISO_URL,g;
        " "$WBATCH_TEMPLATE_DIR/template-begin_base_bat" | wbatch_write_stdin
}

function wbatch_begin_node {
    local node_name=$1
    wbatch_new_file "create_${node_name}_node.bat"
    wbatch_file_header "$node_name VM"
    wbatch_find_vbm
    wbatch_mkdirs

    local basedisk=$(get_base_disk_name).vdi

    sed -e "
        s,%BASEDISK%,$basedisk,g;
        " "$WBATCH_TEMPLATE_DIR/template-begin_node_bat" | wbatch_write_stdin
}

#-------------------------------------------------------------------------------
# VBoxManage call handling
#-------------------------------------------------------------------------------

function wbatch_log_vbm {
    ARGS=( "$@" )
    for i in "${!ARGS[@]}"; do
        case "${ARGS[i]}" in
            --hostonlyadapter*)
                # The next arg is the host-only interface name -> change it
                ARGS[i+1]=\"%${ARGS[i+1]}%\"
                ;;
            --hostpath)
                # The next arg is the shared dir -> change it
                ARGS[i+1]='%SHAREDIR%'
                continue
                ;;
        esac

        # On Windows, ISO and base disk images must be in IMGDIR
        re='\.(iso|vdi)$'
        if [[ "${ARGS[i]}" =~ $re ]]; then
            local img_name=$(basename "${ARGS[i]}")
            ARGS[i]="%IMGDIR%\\$img_name"
            continue
        fi
    done

    # Echo what we are about to do
    wbatch_write_line "ECHO VBoxManage ${ARGS[@]}"

    wbatch_write_line "\"%VBM%\" ${ARGS[@]}"

    # Abort if VBoxManage call raised errorlevel
    wbatch_write_line "IF %errorlevel% NEQ 0 GOTO :vbm_error"

    # Blank line for readability
    wbatch_write_line ""
}

#-------------------------------------------------------------------------------
# Windows path name helpers
#-------------------------------------------------------------------------------

# On Windows, all paths are relative to TOP_DIR
function wbatch_path_to_windows {
    local full_path=$1
    # strip off ${TOP_DIR}/
    full_path="${full_path/$TOP_DIR\//}"
    full_path=$(wbatch_slash_to_backslash "$full_path")
    echo "$full_path"
}

# Escape backslashes in (path) variables that are given to sed
function wbatch_escape_backslash {
    local string=$1
    string="${string//\\/\\\\}"
    echo "$string"
}

function wbatch_slash_to_backslash {
    local some_path=$1
    some_path="${some_path//\//\\}"
    echo "$some_path"
}

# vim: set ai ts=4 sw=4 et ft=sh:
