#!/bin/bash
# dbwebb: tool to manage course repos
#
# See more: http://dbwebb.se/dbwebb-cli
#
# Author: Mikael Roos, mos@dbwebb.se
#
# The MIT License (MIT)
#
# Copyright (c) 2014-2015 Mikael Roos (mos@dbwebb.se)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#
# Current version
#
DBW_VERSION="v2.0.23 (2018-03-06)"
# --------------- DBWEBB FUNCTIONS PHASE START ---------------

#
# Set correct mode on published file and dirs
#
publishChmod()
{
    local dir="$1"

    if [ -d "$dir" ]; then
        find "$dir" -type d -name 'cache' -exec chmod -R a+rw {} \;
        find "$dir" -type d -name 'db' -exec chmod a+rwx {} \;

        find "$dir" -type f -name '*.conf' -exec chmod go-r {} \;
        find "$dir" -type f -name '*.py' -exec chmod go-r {} \;
        find "$dir" -type f -name 'log.txt' -exec chmod go-r {} \;
        find "$dir" -type f -name '*.sh' -exec chmod go-r {} \;
        find "$dir" -type f -name '*.bash' -exec chmod go-r {} \;
        find "$dir" -type f -name '*.cgi' -exec chmod a+rx {} \;
        find "$dir" -type f -name '*.sqlite' -exec chmod a+rw {} \;
        find "$dir" -type f -name '*.sql' -exec chmod go+r {} \;

        case "$DBW_COURSE" in
            databas)
                find "$dir" -type d -path '*/me/kmom*' -exec chmod -R go-r {} \;
            ;;
            phpmvc)
                find "$dir" -type d -path '*/css/anax-grid' -exec chmod o+w {} \;
                find "$dir" -type f -path '*/css/anax-grid/style.css' -exec chmod o+w {} \;
                find "$dir" -type f -path '*/css/anax-grid/style.less.cache' -exec chmod o+w {} \;
            ;;
            linux)
                find "$dir" -type f -not -path '*/me/redovisa/*' -not -path '*/mysite/*' -name '*.js' -exec chmod go-r {} \;
                find "$dir" -type f -name 'mazerunner' -exec chmod go-r {} \;
                find "$dir" -type f -name '*.txt' -exec chmod go-r {} \;
                find "$dir" -type f -name '*.json' -exec chmod go-r {} \;
            ;;
            oopython \
            | matmod)
                find "$dir" -type f -path '*/db/*.json' -exec chmod go+w {} \;
                find "$dir" -type f -name '*.py' -exec chmod go+r {} \;
                #find "$dir" -type f -name '*.py' -path '*/flask/*' -exec chmod go+r {} \;
                #find "$dir" -type f -name '*.py' -path '*/my_app/*' -exec chmod go+r {} \;
            ;;
        esac
    fi
}



#
# realpath -f dows not work on Mac OS
#
function get_realpath()
{
    # failure : file does not exist.
    [[ ! -e "$1" ]] && return 1

    # do symlinks.
    [[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd'

    # Path to dir, without eventual filename
    local path="$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"

    if [ -d "$1" ]; then
        path="$( cd "$( echo "$1" )" 2>/dev/null; $pwdp )"
    elif [ -f "$1" ]; then
        path="$path/${1##*/}"
    fi

    echo "$path"
    return 0
}



#
# Does key exists in array?
#
function exists() {
    if [ "$2" != in ]; then
        echo "Incorrect usage."
        echo "Correct usage: exists {key} in {array}"
        return
    fi
    eval '[ ${'$3'[$1]+muahaha} ]'
}



#
# Check if array contains a value
#
function contains() {
    local e
    for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
    return 1
}



#
# Join elements with separator
# join , a "b c" d #a,b c,d
# join / var local tmp #var/local/tmp
# join , "${FOO[@]}" #a,b,c
#
function join()
{
    local IFS="$1";
    shift;
    echo "$*";
}



#
# Get the url to GitHub for a repo
#
function createGithubUrl(){
    echo "https://github.com/dbwebb-se/$1$2"
}



#
# Check for installed commands
#
function checkCommand
{
    local COMMAND="$1"

    if ! hash "$COMMAND" 2>/dev/null; then
        printf "Command $COMMAND not found."
    else
        printf "$( which $COMMAND )"
    fi
}



#
# Check command and version of it with nice string as result
#
function checkCommandWithVersion
{
    local what="$1"
    local version="$2"
    local options="$3"

    if ! hash "$what" 2>/dev/null; then
        printf "%-10s Command '%s' not found." "-" "$what"
    else
        if [ ! -z "$version" ]; then
            printf "%-10s %s" "$( eval $what $version $options )" "$( which $what )"
        else
            printf "%-10s %s" "?" "$( which $what )"
        fi
    fi
}




#
# Execute a command in a controlled manner
#
#function wget {
#  if command wget -h &>/dev/null
#  then
#    command wget "$@"
#  else
#    set "${*: -1}"
#    lynx -source "$1" > "${1##*/}"
#  fi
#}



#
# Press enter to continue
#
pressEnterToContinue()
{
    if [[ ! $YES ]]; then
        printf "\nPress enter to continue..."
        read void
    fi
}



#
# Answer yes or no to proceed
#
answerYesOrNo()
{
    local answer="y"
    local default="$1"

    if [[ ! $YES ]]; then
        read answer
        answer=${answer:-$default}
    fi

    echo "$answer"
}



#
# Execute a command in a controlled manner
#
function executeCommandInSubShell
{
    executeCommand "$1" "$2" "$3" "$4" "subshell"
}



#
# Execute a command in a controlled manner
#
function executeCommand
{
    local introText="$1"
    local subshell="$5"
    
    # Introduction text for the command
    echo "$introText"

    REALLY="$4"
    if [ ! -z $REALLY ]
    then
        printf "\nAre you really sure? [yN] "
        read answer
        default="n"
        answer=${answer:-$default}

        if [ ! \( "$answer" = "y" -o "$answer" = "Y" \) ]
        then
            printf "Exiting...\n"
            exit 0
        fi
    fi

    COMMAND="$2"

    if [[ $VERY_VERBOSE ]]
    then
        printf "\nExecuting command:"
        printf "\n$COMMAND"
        printf "\n-----------------------------------------"
        printf "\n"
    fi

    if [ "$subshell" = "subshell" ]; then
        ( "$COMMAND" )
    else
        bash -c "$COMMAND"
    fi
    STATUS=$?

    if [[ $VERY_VERBOSE ]]
    then
        printf "\n-----------------------------------------"
        printf "\n"
    fi

    MESSAGE=$3
    if [[ ! $SILENT ]]; then
        if [ $STATUS = 0 ]
        then
            printf "$MSG_DONE $MESSAGE"
        else
            printf "$MSG_FAILED $MESSAGE"
        fi
        printf "\n"
    fi

    return $STATUS
}



#
# Convert version to a compareble string
# Works for 1.0.0 and v1.0.0
#
function getSemanticVersion
{
    #local version=${1:1}
    local version=$( echo $1 | sed s/^[vV]// )
    echo "$version" | awk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'
}



#
# Check if current version of dbwebb-cli matches minimum required version.
#
function checkMinimumVersionRequirementOrExit
{
    if [[ $DBW_VERSION_REQUIREMENT ]]; then
        local required current
        required=$( getSemanticVersion $DBW_VERSION_REQUIREMENT )
        current=$( getSemanticVersion $DBW_VERSION )
        if [ "$current" -lt "$required" ]; then
            printf "$MSG_FAILED You need to upgrade dbwebb-cli to work with this course repo.\nDo a 'dbwebb selfupdate'.\n"
            printf "Your current version is:     %s\n" "$DBW_VERSION"
            printf "Minimum required version is: %s\n" "$DBW_VERSION_REQUIREMENT"
            exit 1
        fi
    fi
}



#
# Check if within a valid course repo or exit
#
function checkIfValidCourseRepoOrExit
{
    if [ "$DBW_COURSE_REPO_VALID" != "yes" ]; then
        printf "$MSG_FAILED Could not find file '%s', this is not a valid course repo." "$DBW_COURSE_FILE_NAME"
        printf "\nThis command must be executed within a valid course repo."
        printf "\n"
        exit 1
    fi

    checkMinimumVersionRequirementOrExit
}



#
# Check if config file or exit
#
function checkIfValidConfigOrExit()
{
    if [ ! -f "$DBW_CONFIG_FILE" ]; then
        printf "$MSG_FAILED Could not find the configuration file '$DBW_CONFIG_FILE', this is needed for this operation."
        printf "\n"
        exit 1
    fi
}



#
# Set proper rights for files and directories
# OBSOLETE to be replaced by rsync --chmod
#
setChmod()
{
    if [[ $VERY_VERBOSE ]]; then
        printf "Ensuring that all files and directories are readable for all, below $DBW_COURSE_DIR.\n"
    fi

    find "$DBW_COURSE_DIR" -type d -exec chmod u+rwx,go+rx {} \;
    find "$DBW_COURSE_DIR" -type f -exec chmod u+rw,go+r {} \;
}



#
# Convert course specific module to path on disk
#
mapCmdToDir()
{
    local CMD="$1"
    local RES=""

    if [ -z "$CMD" ]; then
        return
    fi

    case "$CMD" in
        example)    RES="example" ;;
        me)         RES="me" ;;
        redovisa)   RES="me/redovisa" ;;
        kmom01)     RES="me/kmom01" ;;
        kmom02)     RES="me/kmom02" ;;
        kmom03)     RES="me/kmom03" ;;
        kmom04)     RES="me/kmom04" ;;
        kmom05)     RES="me/kmom05" ;;
        kmom06)     RES="me/kmom06" ;;
        kmom10)     RES="me/kmom10" ;;
    esac

    [ ! -z $RES ] && echo "$RES" && return

    # Check for known pathes in course repo config file
    local mapFile="$DBW_COURSE_DIR/.dbwebb.map"
    local item=
    if [ -f "$mapFile" ]; then
        while IFS= read -r path
        do
            item=$( basename "$path" )
            if [ "$CMD" = "$item" -a -d "$path" ]; then
                echo "$path"
                return
            fi
        done < <(grep "^[^#]" "$mapFile")
    fi

    # Check for existing directories below me/
    [ -d "$DBW_COURSE_DIR/me/$CMD" ] && echo "me/$CMD" && return 

    # Check me/* for match
    RES=$( cd "$DBW_COURSE_DIR" && find me -mindepth 2 -maxdepth 2 -name "$CMD" -type d | head -n 1 )

    echo "$RES"
    return
}



#
# Get path to dir to check, use both parts of courses and fallback
# to absolute and relative paths.
#
function createDirsInMeFromMapFile
{
    local mapFile="$DBW_COURSE_DIR/.dbwebb.map"
    if [ -f "$mapFile" ]; then
        while IFS= read -r path
        do
            echo "$path"
            install -d "$DBW_COURSE_DIR/$path"
        done < <(grep "^[^#]" "$mapFile")
    fi
}



#
# Get path to dir to check, use both parts of courses and fallback
# to absolute and relative paths.
#
function getPathToDirectoryFor
{
    local dir="$( mapCmdToDir $1 )"

    if [ -z "$command" ]; then
        echo "$DBW_CURRENT_DIR"
    elif [ -z "$dir" -a -d "$command" ]; then
        echo "$command"
    elif [ -z "$dir" -a -d "$DBW_CURRENT_DIR/$command" ]; then
        echo "$DBW_CURRENT_DIR/$command"
    elif [ ! -z "$dir" -a -d "$DBW_COURSE_DIR" -a -d "$DBW_COURSE_DIR/$dir" ]; then
        echo "$DBW_COURSE_DIR/$dir"
    else
        printf "\n$MSG_FAILED The item '$command' was mapped to directory '$dir' which is not a valid directory.\n"
        exit 1
    fi
}



#
# Validate the uploaded files
#
createUploadDownloadPaths()
{
    local ignoreSubdir="$1"
    SUBDIR="$( mapCmdToDir $ITEM )"

    if [ -z "$WHAT" -o -z "$WHERE" ]; then
        printf "$MSG_FAILED Missing argument for source or destination. Perhaps re-create the config-file?"
        printf "\n\n"
        exit 1
    fi

    #if [ -d "$DBW_CURRENT_DIR/$ITEM" ]; then
    #    SUBDIR="${ITEM%/}"
    #elif [ ! -z "$ITEM" -a -z "$SUBDIR" ]; then
    if [ ! -z "$ITEM" -a -z "$SUBDIR" ]; then
        printf "\n$MSG_FAILED Not a valid combination course: '$DBW_COURSE' and item: '$ITEM'."
        printf "\n\n"
        exit 1
    fi

    if [ ! -z "$ignoreSubdir" ]; then
        WHAT="$WHAT/"
        WHERE="$WHERE/"
    elif [ ! -z "$SUBDIR" ]; then
        WHAT="$WHAT/$SUBDIR/"
        WHERE="$WHERE/$SUBDIR/"
    else
        WHAT="$WHAT/"
        WHERE="$WHERE/"
    fi

    #echo "WHAT=$WHAT"
    #echo "WHERE=$WHERE"

    if [ ! -d "$WHAT" ]; then
        printf "\n$MSG_FAILED Target directory is not a valid directory: '$WHAT'"
        printf "\n\n"
        exit 1
    fi
}



#
# Selfupdate
#
selfupdate()
{
    local what="$1"
    local version="$2"
    local target="$DBW_EXECUTABLE_PATH"
    local remote=
    local silent="--quiet"
    local repo="https://raw.githubusercontent.com/mosbth/dbwebb-cli/master"

    if [[ $VERY_VERBOSE ]]; then
        silent=""
    fi

    [[ $version ]] && repo="$repo/release/$version/"

    case $what in
        dbwebb)
            remote="$repo/dbwebb2"
        ;;

        dbwebb-validate)
            remote="$repo/dbwebb2-validate"
        ;;

        dbwebb-inspect)
            remote="$repo/dbwebb2-inspect"
        ;;
    esac

    printf "Your current version is: "
    $what --version

    echo "Selfupdating '$what' from $repo"

    # Downloading
    # printf '\nDownloading... '; wget $silent $remote -O /tmp/$$;
    if hash wget 2> /dev/null; then
        local dli="Downloading using wget... "
        local dlc="wget $silent $remote -O /tmp/$$"
    else
        echo "Failed. Did not find wget. Please install wget."
        exit 1
    fi
    local dlm="to download."
    executeCommand "$dli" "$dlc" "$dlm"
    [[ $STATUS > 0 ]] && echo "Could not download the remote file, check the download link if its correct." && exit 1

    # Installing
    # printf '\nInstalling... '; install /tmp/$$ $target;
    local ini="Installing... "
    local inc="install /tmp/$$ $target"
    local inm="to install."
    executeCommand "$ini" "$inc" "$inm"
    local ins=$?

    # Cleaning up
    # printf '\nCleaning up... '; rm /tmp/$$;
    local cli="Cleaning up... "
    local clc="rm /tmp/$$"
    local clm="to clean up."
    executeCommand "$cli" "$clc" "$clm"

    if [ $ins != 0 ]; then
        exit 1
    fi

    printf "The updated version is now: "
    $what --version
}



#
# Perform an assert
#
function assert()
{
    EXPECTED=$1
    TEST=$2
    MESSAGE=$3
    ASSERTS=$(( $ASSERTS + 1 ))
    local onlyExitStatus=$4
    local error=
    local status=

    bash -c "$TEST" &> "$TMPFILE"
    status=$?

    if [ \( -z "$onlyExitStatus" \) -o \( ! $status -eq $EXPECTED \) ]; then
        error=$( cat "$TMPFILE" )
    fi
    rm -f "$TMPFILE"

    if [ \( ! $status -eq $EXPECTED \) -o \( ! -z "$error" \) ]; then
        FAULTS=$(( $FAULTS + 1 ))

        printf "\n\n$MSG_WARNING %s\n" "$MESSAGE"
        [ -z "$error" ] || printf "%s\n\n" "$error"
    fi

    return $status
}



#
# Perform an assert on exit value returned
# TODO Check if this is really needed by python inspect
#
assertExit()
{
    EXPECTED=$1
    TEST=$2
    MESSAGE=$3
    ASSERTS=$(( $ASSERTS + 1 ))

    bash -c "$TEST" &> "$TMPFILE"
    STATUS=$?
    ERROR=$( cat "$TMPFILE" )
    rm -f "$TMPFILE"

    if [ $STATUS -ne $EXPECTED ]; then
        FAULTS=$(( $FAULTS + 1 ))

        printf "\n$TEST"
        printf "\n\n$MSG_FAILED $MESSAGE\n"
        [ -z "$ERROR" ] || printf "$ERROR\n\n"

        return 1
    fi

    return 0

}




#
# Clean up and output results from asserts
#
function assertResults()
{
    if [ $FAULTS -gt 0 ]
        then
        printf "\n\n$MSG_FAILED"
        printf " Asserts: $ASSERTS Faults: $FAULTS\n\n"
        exit 1
    fi

    printf "\n$MSG_OK"
    printf " Asserts: $ASSERTS Faults: $FAULTS\n"
    exit 0
}



# --------------- DBWEBB FUNCTIONS PHASE END ---------------
# --------------- DBWEBB BOOTSTRAP PHASE START ---------------

# Messages
MSG_OK="\033[0;30;42mOK\033[0m"
MSG_DONE="\033[1;37;40mDONE\033[0m"
MSG_WARNING="\033[43mWARNING\033[0m"
MSG_FAILED="\033[0;37;41mFAILED\033[0m"

# For asserts
ASSERTS=0
FAULTS=0
TMPFILE="/tmp/dbwebb-error-{$USER}-$$"



#
# Find my environment, before any work can be done
#


# What was the command issued?
DBW_EXECUTABLE_PATH="$0"
DBW_EXECUTABLE="$( basename "$0" )"

# Where is the executable
DBW_EXECUTABLE_DIR="$( dirname "$0" )"

# What is the current directory
DBW_CURRENT_DIR="$( pwd )"



# What is the directory of the current course repo, find recursivly up the tree
DBW_COURSE_FILE_NAME=".dbwebb.course"

dir="$DBW_CURRENT_DIR/."
while [ "$dir" != "/" ]; do
    dir=$( dirname "$dir" )
    found="$( find "$dir" -maxdepth 1 -name $DBW_COURSE_FILE_NAME )"
    if [ "$found" ]; then 
        DBW_COURSE_DIR="$( dirname "$found" )"
        break
    fi
done



#
# Get the name of the course as $DBW_COURSE
#
function sourceCourseRepoFile
{
    DBW_COURSE_FILE="$DBW_COURSE_DIR/$DBW_COURSE_FILE_NAME"
    DBW_COURSE_REPO_VALID=""
    if [ -f "$DBW_COURSE_FILE" ]; then
        DBW_COURSE_REPO_VALID="yes"
        source "$DBW_COURSE_FILE"
    fi    
}

# Get the name of the course as $DBW_COURSE
sourceCourseRepoFile



#
# Get the name of the course as $DBW_COURSE
#
function sourceDbwebbVersionFile
{
    DBW_VERSION_FILE="$DBW_COURSE_DIR/$DBW_VERSION_FILE_NAME"
    DBW_VERSION_REQUIREMENT=""
    if [ -f "$DBW_VERSION_FILE" ]; then
        DBW_VERSION_REQUIREMENT=$(< "$DBW_VERSION_FILE" )
    fi
}

# Get the proposed minimum version for dbwebb-cli in current course repo
DBW_VERSION_FILE_NAME=".dbwebb/cli.version"
sourceDbwebbVersionFile



# Where is the .dbwebb.config-file
DBW_CONFIG_FILE_NAME=".dbwebb.config"

# Check if run as sudo, use SUDO_USER as HOME (only for selfupdate)
if [[ $SUDO_USER ]]; then
    DBW_CONFIG_FILE=$( eval echo "~$SUDO_USER/$DBW_CONFIG_FILE_NAME" )
else    
    DBW_CONFIG_FILE="$HOME/$DBW_CONFIG_FILE_NAME"
fi

if [ -f "$DBW_CONFIG_FILE" ]; then
    source "$DBW_CONFIG_FILE"
fi



# Detect if ssh-key is available
DBW_SSH_KEY_OPTION=""
if [ -f "$DBW_SSH_KEY" ]
then
    DBW_SSH_KEY_OPTION="-i '$DBW_SSH_KEY'"
fi



# Check OS
DBW_OS="$( uname -a )"

# Create the ssh-command with details from the config-file
SSH_CMD="ssh ${DBW_USER}@${DBW_HOST} $DBW_SSH_KEY_OPTION"
SSH_CMD_INTERACTIVE="ssh ${DBW_USER}@${DBW_HOST} $DBW_SSH_KEY_OPTION"

# Default chmod for rsync-command 
RSYNC_CHMOD="--chmod=Du+w,Dgo+rx,Fu+rw,Fgo+r"

# Prefer using file to exclude from
RSYNC_EXCLUDE="$DBW_COURSE_DIR/.dbwebb/upload.exclude"
if [ -f "$RSYNC_EXCLUDE" ]; then
    RSYNC_EXCLUDE="--exclude-from='$RSYNC_EXCLUDE'"
else
    RSYNC_EXCLUDE="--exclude .git --exclude .gitignore --exclude literature --exclude tutorial --exclude slide --exclude .solution --exclude old --exclude .default --exclude platforms/ --exclude coverage/ --exclude .DS_Store --exclude npm-debug.log"
fi

# Use filt to include from, if available
RSYNC_INCLUDE="$DBW_COURSE_DIR/.dbwebb/upload.include"
if [ -f "$RSYNC_INCLUDE" ]; then
    RSYNC_INCLUDE="--include-from='$RSYNC_INCLUDE'"
else
    RSYNC_INCLUDE=
fi

# Create the basis for the upload command
RSYNC_CMD="rsync -av $RSYNC_CHMOD $RSYNC_INCLUDE $RSYNC_EXCLUDE --delete --delete-excluded -e \"ssh $DBW_SSH_KEY_OPTION\""
RSYNC_DOWNLOAD_DELETE_CMD="rsync -avc $RSYNC_CHMOD --delete --delete-excluded -e \"ssh $DBW_SSH_KEY_OPTION\""
RSYNC_DOWNLOAD_CMD="rsync -avuc $RSYNC_CHMOD -e \"ssh $DBW_SSH_KEY_OPTION\""

DBW_REMOTE_DESTINATION="${DBW_USER}@${DBW_HOST}:$DBW_REMOTE_BASEDIR/$DBW_COURSE"
DBW_REMOTE_WWW_DESTINATION="${DBW_USER}@${DBW_HOST}:$DBW_REMOTE_WWWDIR/$DBW_COURSE"

DBW_WWW="https://dbwebb.se/"
DBW_VALIDATE_CONFIGFILE="/home/saxon/students/dbwebb/dbwebb-general-config"
#DBW_INSPECT_CONFIGFILE="/home/saxon/students/dbwebb/dbwebb-general-config"
DBW_ARCHIVE="/home/saxon/students/dbwebb/archive/"


# Ass arrays not supporter on mac bash v3.
#declare -A DBW_REPOS
#DBW_REPOS[python]="https://github.com/mosbth/python"
#DBW_REPOS[javascript1]="https://github.com/mosbth/javascript1"
#DBW_REPOS[linux]="https://github.com/mosbth/linux"
#DBW_REPOS[webapp]="https://github.com/mosbth/webapp"
#DBW_REPOS[htmlphp]="https://github.com/mosbth/htmlphp"
DBW_COURSE_REPOS=( 'python' 'javascript1' 'linux' 'webapp' 'oopython' 'htmlphp' 'design' 'oophp' 'phpmvc' 'javascript' 'webgl' 'dbjs' 'ramverk1' 'ramverk2' 'databas' 'matmod' 'exjobbd')
DBW_REPO="dbwebb-cli"


# --------------- DBWEBB BOOTSTRAP PHASE END ---------------
function usage ()
{
    local txt=(
"Utility dbwebb for working with course repos: https://dbwebb.se/dbwebb-cli"
"Usage: dbwebb [options] <command> [arguments]"
""
"Command:"
"  check                Check the environment."
"  config               (Re-)Create config file."
"  selfupdate [version] Update to latest (or selected) version."
"  sshkey               Create and install ssh-keys."
"  login                Login to the remote server."
"  clone [repo]         Clone a course repo."
"  init                 Init course repo and remote server."
"  github [repo]        Get urls to repo on GitHub."
"  update               Update course repo."
"  upload [part]        Upload to server."
"  download [part]      Download from server."
"  create <lab> [version] Create a lab using optional version."
"  recreate <lab>       Re-create lab with optional version."
"  validate [part]      Validate it."
"  publish [part]       Publish it."
"  publishfast [part]   Publish without validation."
"  publishpure [part]   Publish without minification."
"  inspect [course] [kmom] [user]  Inspect a kmom."
"  run <command>        Run a command on remote host."
"  testrepo             Automated tests of a course repo."
""
"Options:"
"  --inspect, -i  Help for inspect."
"  --verbose, -v  More verbose."
"  --silent, -s   Less verbose output."
"  --force, -f    Force upload/download of files, overwrite existing."
"  --local, -l    Only local tests in command testrepo."
"  --host         Host to connect to (supported by run)."
"  --cwd          Working dir for command (supported by run)."
"  --dry          Dry run (supported by validate/publish)."
"  --no-validate  Skip validation when --yes in inspect."
"  --help, -h     Print help."
"  --version      Print version."
    )
    printf "%s\n" "${txt[@]}"
}



function badUsage ()
{
    local message="$1"
    local txt=(
"For an overview of the command, execute:"
"dbwebb --help"
    )
    
    if [[ "$message" ]]; then
        printf "$message\n"
    fi
    
    printf "%s\n" "${txt[@]}"
}



function usageGithub ()
{
    local txt=(
"Available course repos are: $( join , "${DBW_COURSE_REPOS[@]}" )"
"Usage: dbwebb github [course repo]"
""
"The repo 'dbwebb-cli' exists on GitHub."
"Repo:   $( createGithubUrl "$DBW_REPO" )"
"Issues: $( createGithubUrl "$DBW_REPO" "/issues" )"
)
    printf "%s\n" "${txt[@]}"
}



function badUsageGithub ()
{
    local message="$1"

    if [[ "$message" ]]; then
        printf "$message\n"
    fi
    
    usageGithub
}


function usageClone ()
{
    local txt=(
"Available course repos are: $( join , "${DBW_COURSE_REPOS[@]}" )"
"Usage: dbwebb clone [course repo]"
"Read more: http://dbwebb.se/dbwebb-cli/clone"
)
    printf "%s\n" "${txt[@]}"
}



function badUsageClone ()
{
    local message="$1"

    if [[ "$message" ]]; then
        printf "$message\n"
    fi
    
    usageClone
}



function usageInspect ()
{
    local txt=(
"dbwebb inspect:"
"Use to inspect a kmom, as the teachers does, self inspect your own kmom before handing it in."
""
"Usage: dbwebb inspect <kmom>"
"Use to self inspect a kmom in current course repo. This will also upload all your files to the remote server to ensure you are checking the latest code."
""
"Usage: dbwebb inspect <course> <kmom>"
"Use to inspect a kmom in any course repo that is uploaded to the remote server. No need of being in a valid course repo. No upload of files."
""
"Usage: dbwebb inspect <course> <kmom> <user>"
"Use to inspect a kmom in any course for any user, without the need of being in a valid course repo. No upload of files."
)
    printf "%s\n" "${txt[@]}"
}



function version ()
{
    local txt=(
"dbwebb version $DBW_VERSION"
    )
    printf "%s\n" "${txt[@]}"
}



function inspectUsage ()
{
    local txt=(
"Using dbwebb inspect, for another student than youreself,"
"needs extra privilegies."
""
"Add acronym for inspect"
" sudo /usr/local/sbin/setpre-dbwebb-kurser.bash acronym"
""
"or add as an alias in your .cshrc"
" dbwebb run echo 'alias potatoe \"sudo setpre-dbwebb-kurser.bash\"' >> .cshrc"
""
"and then run as this:"
" dbwebb run potatoe akronym"
""
"Add or delete teacher (on each server)"
" sudo update-dbwebb-kurser.bash -a acronym"
" sudo update-dbwebb-kurser.bash -d acronym"
""
"Change owner to mosstud of all files in the archive"
" sudo /usr/local/sbin/setowner-on-dbwebb-archive.bash"
""
"Add --yes to answer yes/default answer to all questions."
""
" Add --port port, -p port to execute external servers on this port."
""
"Add --useVersion 1 to use a specific version of the course. Useful when courses has several versions and same repo must be used for all versions."
""
"Execute command as the user dbwebb."
" sudo -u dbwebb script"
    )
    printf "%s\n" "${txt[@]}"
}
#------------------------- HERE ARE THE MAIN COMMANDS -----------------------


#
# Create the config file .dbwebb.config.
#
function createConfig()
{
    local first=$1
    local noInput=$2
    local acronym
    local remoteHost

    if [ -z $first ]
    then

        printf "The config-file '$DBW_CONFIG_FILE_NAME' will now be created in your home directory: '$HOME'"

    elif [ $first = "update" ]
    then

        printf "Your config file will be automatically updated. Then re-run your command.\n"

    elif [ $first = "upgrade" ]
    then

        printf "Your config file will be automatically updated."

        # Temporary to solve upgrades from v1.9.29 to v1.9.32
        unset DBW_SSH_KEY

    elif [ $first = "selfupdate" ]
    then

        # Temporary to solve upgrades from v1.9.29 to v1.9.32
        unset DBW_SSH_KEY

    elif [ $first = "create" ]
    then

        printf "I will now re-create the configuration file '$DBW_CONFIG_FILE_NAME' in your home directory: '$HOME'.\n"

    fi


    if [[ ! $noInput ]]; then
        DBW_USER=${DBW_USER:-$USER}
        printf "\nWhat is your student acronym? [$DBW_USER] "
        read acronym
        unset DBW_BASEURL
    else
        DBW_USER=${DBW_USER:-noname}
    fi

    acronym=${acronym:-$DBW_USER}
    remoteHost=${DBW_HOST:-ssh.student.bth.se}
    sshKey=${DBW_SSH_KEY:-\$HOME/.ssh/dbwebb}
    remoteDir=${DBW_REMOTE_BASEDIR:-dbwebb-kurser}
    remoteWwwHost=${DBW_WWW_HOST:-http://www.student.bth.se/}
    remoteWww=${DBW_REMOTE_WWWDIR:-www/dbwebb-kurser}
    baseurl=${DBW_BASEURL:-http://www.student.bth.se/~$acronym/$remoteDir}
    laburl=${DBW_LABURL:-http://www.student.bth.se/~mosstud/kod-exempel/lab}

    echo "DBW_USER='$acronym'"               > "$DBW_CONFIG_FILE"
    echo "DBW_HOST='$remoteHost'"           >> "$DBW_CONFIG_FILE"
    echo "DBW_WWW_HOST='$remoteWwwHost'"    >> "$DBW_CONFIG_FILE"
    echo "DBW_SSH_KEY=\"$sshKey\""          >> "$DBW_CONFIG_FILE"
    echo "DBW_REMOTE_BASEDIR='$remoteDir'"  >> "$DBW_CONFIG_FILE"
    echo "DBW_REMOTE_WWWDIR='$remoteWww'"   >> "$DBW_CONFIG_FILE"
    echo "DBW_BASEURL='$baseurl'"           >> "$DBW_CONFIG_FILE"
    echo "DBW_LABURL='$laburl'"             >> "$DBW_CONFIG_FILE"

    printf "$MSG_DONE The config file '$DBW_CONFIG_FILE' is now up-to-date.\n"
}



#
# Create default files.
#
function dbwebb-init-me()
{
    checkIfValidCourseRepoOrExit

    local mapFile="$DBW_COURSE_DIR/.dbwebb.map"
    if [ -f "$mapFile" ]; then
        local intro="Creating and initiating the directory structure in 'me/' (will not overwrite existing files)."
        local command="createDirsInMeFromMapFile"
        local message="to init the directory 'me/'."
        executeCommandInSubShell "$intro" "$command" "$message"
        return
    fi

    local meDefault="$DBW_COURSE_DIR/.default/"
    local me="$DBW_COURSE_DIR/me/"

    local intro="Creating and initiating the directory 'me/' by copying directory structure and files from the directory '.default/' (will not overwrite existing files)."
    local command="rsync -av $RSYNC_CHMOD --exclude README.md --ignore-existing \"$meDefault\" \"$me\""
    local message="to init the directory 'me/'."

    executeCommand "$intro" "$command" "$message"
}



#
# Init directory structure at the server.
#
function dbwebb-init-server()
{    
    local intro="Intiating the remote server '$DBW_HOST' by connecting as '$DBW_USER' and creating directories (if needed) where all uploaded files will reside."
    # TODO Should use DBW_BASEDIR 
    local command="$SSH_CMD 'sh -c \"if [ ! -d \"$DBW_REMOTE_BASEDIR\" ]; then mkdir \"$DBW_REMOTE_BASEDIR\"; fi; chmod 700 \"$DBW_REMOTE_BASEDIR\"; echo; echo \"$DBW_REMOTE_BASEDIR:\"; ls -lF \"$DBW_REMOTE_BASEDIR\"; if [ ! -d \"$DBW_REMOTE_WWWDIR\" ]; then mkdir \"$DBW_REMOTE_WWWDIR\"; fi; chmod 755 \"$DBW_REMOTE_WWWDIR\"; echo; echo \"$DBW_REMOTE_WWWDIR:\"; ls -lF \"$DBW_REMOTE_WWWDIR\"\"'"
    local message="to init the base dirs on the server."

    checkIfValidConfigOrExit
    executeCommand "$intro" "$command" "$message"
}



#
# Create default directory structure on the server for dbwebb-kurser/.
#
function dbwebb-init-structure-dbwebb-kurser()
{
    local intro="Ensuring that the directory structure exists on the server by syncing directory structure to dbwebb-kurser/ (will not overwrite existing files)."
    local command=
    local message="to init the directory structure on the server."

    checkIfValidConfigOrExit
    checkIfValidCourseRepoOrExit

    WHAT="$DBW_COURSE_DIR"
    WHERE="$DBW_REMOTE_DESTINATION"
    ITEM=""
    SUBDIR=""

    createUploadDownloadPaths

    command="rsync -av $RSYNC_CHMOD --exclude example $RSYNC_INCLUDE $RSYNC_EXCLUDE --include='*/' --include='.??*' --exclude='*' -e \"ssh $DBW_SSH_KEY_OPTION\" '$WHAT' '$WHERE'"
    executeCommand "$intro" "$command" "$message"
}



#
# Create default directory structure on the server for dbwebb-kurser/.
#
function dbwebb-init-structure-www-dbwebb-kurser()
{
    local intro="Ensuring that the directory structure exists on the server by syncing the me/ directory structure to www/dbwebb-kurser (will not overwrite existing files)."
    local command=
    local message="to init the www-directory structure on the server."

    checkIfValidConfigOrExit
    checkIfValidCourseRepoOrExit

    WHAT="$DBW_COURSE_DIR"
    WHERE="$DBW_REMOTE_WWW_DESTINATION"
    ITEM=""
    SUBDIR=""

    createUploadDownloadPaths

    command="rsync -av $RSYNC_CHMOD --exclude example $RSYNC_INCLUDE $RSYNC_EXCLUDE --include='*/' --exclude='*' -e \"ssh $DBW_SSH_KEY_OPTION\" '$WHAT' '$WHERE'"
    executeCommand "$intro" "$command" "$message"
}



#
# Init course repo and directory structure at the server.
#
function dbwebb-init()
{    
    dbwebb-init-me
    dbwebb-init-server
    dbwebb-init-structure-dbwebb-kurser
    dbwebb-init-structure-www-dbwebb-kurser
}



#
# Create and use ssh-keys to login.
#
function dbwebb-sshkey()
{
    local sshkey="$HOME/.ssh/dbwebb"

    if [ ! -d "$HOME/.ssh" ]
    then
        mkdir "$HOME/.ssh"
    fi

    printf "First we need to create a ssh key and store it locally."
    printf "\nPress enter/return to continue..."
    read void
    ssh-keygen -f "$sshkey" -N ''
    
    # Bug (?) om Cygwin & win 8
    # TODO refactor 
    #if [ $IS_CYGWIN = "yes" ]; then
    #    chgrp -vR "$CYGWIN_DEFAULT_GROUP" "$HOME/.ssh"
    #fi

    chmod 700 "$HOME/.ssh"
    chmod 600 "$sshkey" "$sshkey.pub"

    intro="I will now install the ssh-key at the remote server."
    command="cat '$sshkey.pub' | ssh $DBW_USER@$DBW_HOST 'sh -c \"if [ ! -d .ssh ]; then mkdir .ssh; fi; chmod 700 .ssh; touch .ssh/authorized_keys; cat >> .ssh/authorized_keys\"'"
    message="to install the ssh-keys."    
    executeCommand "$intro" "$command" "$message"
}



#
# Login to the server
#
function dbwebb-login()
{
    local intro="I will now login to the server '$DBW_HOST' as '$DBW_USER' using ssh-keys if available."
    local command="$SSH_CMD"
    local message="to establish the connection."
    
    checkIfValidConfigOrExit
    executeCommand "$intro" "$command" "$message"
}



#
# Update course repo to latest version
#
function dbwebb-update()
{
    local intro="Update course-repo with latest changes from its master at GitHub."
    local command="git pull"
    local message="to update course repo."
    
    checkIfValidCourseRepoOrExit
    executeCommand "$intro" "$command" "$message"
}



#
# Display information on the environment
#
function dbwebb-check()
{
    printf "Details on installed utilities."
    printf "\n------------------------------------"
    printf "\nbash:               %s" "$( checkCommand bash )"
    printf "\ngit:                %s" "$( checkCommand git )"
    printf "\nssh:                %s" "$( checkCommand ssh )"
    printf "\nrsync:              %s" "$( checkCommand rsync )"
    printf "\nwget:               %s" "$( checkCommand wget )"
    printf "\ncurl:               %s" "$( checkCommand curl )"
    printf "\n"

    printf "\nDetails on the dbwebb-environment."
    printf "\n------------------------------------"
    printf "\nOperatingsystem:    $DBW_OS"
    printf "\nCommand issued:     $DBW_EXECUTABLE"
    printf "\nVersion of dbwebb:  $DBW_VERSION"
    printf "\nPath to executable: '$DBW_EXECUTABLE_DIR'"
    printf "\nConfig-file:        '$DBW_CONFIG_FILE'"
    printf "\nWorking directory:  '$DBW_CURRENT_DIR'"
    printf "\nLocal user:         '$USER'"
    printf "\nLocal homedir:      '$HOME'"
    printf "\nRemote user:        '$DBW_USER'"
    printf "\nRemote host:        '$DBW_HOST'"
    printf "\n"

    echo
    echo "Details on current course-repo."
    echo "------------------------------------"

    if [ "$DBW_COURSE_REPO_VALID" = "yes" ]; then
        echo "Current course-repo:   '$DBW_COURSE'"
        echo "Course directory:      '$DBW_COURSE_DIR'"
        echo "Course-repo version:   $( cd $DBW_COURSE_DIR && git describe --always )"
        echo "Latest update to course repo was:"
        echo 
        (cd "$DBW_COURSE_DIR"; git log -1)
        echo
    else
        echo "This is not a valid course repo."
        echo
    fi

    if contains python "$@"; then
        echo "Details on Python installed utilities."
        echo "------------------------------------"
        echo "python3:            $( checkCommand python3 )"
        echo "pip3:               $( checkCommand pip3 )"
        echo 
    fi
}



#
# Create or re-create the config file.
#
function dbwebb-config()
{
    createConfig $1 $2
}



#
# Push/upload results to the server
#
function dbwebb-upload()
{
    WHAT="$DBW_COURSE_DIR"
    WHERE="$DBW_REMOTE_DESTINATION"
    ITEM="$1"
    SUBDIR=""

    checkIfValidConfigOrExit
    checkIfValidCourseRepoOrExit
    createUploadDownloadPaths ignoreSubdir
    #setChmod

    local intro="Uploading the directory '$WHAT' to '$WHERE'."
    local command="$RSYNC_CMD $OVERWRITE '$WHAT' '$WHERE'"
    local message="to upload data."
    executeCommand "$intro" "$command" "$message"
}



#
# Pull/download from the server
#
function dbwebb-download()
{
    WHAT="$DBW_COURSE_DIR"
    WHERE="$DBW_REMOTE_DESTINATION"
    ITEM="$1"
    SUBDIR=""
    local who="$2"
    local really="really?"

    if [ "$who" != "$DBW_USER" ]; then
        WHERE="${DBW_USER}@${DBW_HOST}:~$who/$DBW_REMOTE_BASEDIR/$DBW_COURSE"
    fi
    
    checkIfValidConfigOrExit
    checkIfValidCourseRepoOrExit
    createUploadDownloadPaths

    local command=
    local overwrite=
    
    if [[ $OVERWRITE ]]; then
        command="$RSYNC_DOWNLOAD_DELETE_CMD $OVERWRITE '$WHERE' '$WHAT'"
        overwrite="WILL BE"
    else
        command="$RSYNC_DOWNLOAD_CMD '$WHERE' '$WHAT'"
        overwrite="WILL NOT BE"
    fi

    [[ $YES ]] && really=

    local intro="Downloading the directory '$WHERE' to '$WHAT'. Existing local files that are newer $overwrite overwritten."
    local message="to download data."
    executeCommand "$intro" "$command" "$message" "$really"
}


#
# Validate the uploaded files
#
function dbwebb-validate()
{
    checkIfValidConfigOrExit
    checkIfValidCourseRepoOrExit
    #setChmod

    local dry=

    [[ $OPTION_DRY ]] && dry="--dry"

    WHAT="$DBW_COURSE_DIR"
    WHERE="$DBW_REMOTE_DESTINATION"
    ITEM="$1"
    SUBDIR=""
    createUploadDownloadPaths ignoreSubdir

    local log="$HOME/.dbwebb-validate.log"
    local intro="Uploading the directory '$WHAT' to '$WHERE' for validation."
    local command1="$RSYNC_CMD $OVERWRITE '$WHAT' '$WHERE'"
#    local command2="rsync -av $RSYNC_CHMOD $OVERWRITE --exclude .git --exclude .gitignore --exclude .default --exclude .solution --exclude .old --include='.??*' --exclude='*' -e \"ssh $DBW_SSH_KEY_OPTION\" '$DBW_COURSE_DIR/' '$DBW_REMOTE_DESTINATION/'"
    local command3="$SSH_CMD 'dbwebb-validate1 $dry --course-repo \"$DBW_REMOTE_BASEDIR/$DBW_COURSE\" \"$DBW_REMOTE_BASEDIR/$DBW_COURSE/$SUBDIR\"' 2>&1 | tee '$log'; test \${PIPESTATUS[0]} -eq 0"
    local message="to validate course results.\nSaved a log of the output: less -R '$log'"
    #executeCommand "$intro" "$command1; $command2; $command3" "$message"
    executeCommand "$intro" "$command1; $command3" "$message"
}



#
# Publish the uploaded files
#
function dbwebb-publish()
{
    checkIfValidConfigOrExit
    checkIfValidCourseRepoOrExit
    #setChmod

    [[ $OPTION_DRY ]] && dry="--dry"

    WHAT="$DBW_COURSE_DIR"
    WHERE="$DBW_REMOTE_DESTINATION"
    ITEM="$1"
    SUBDIR=""
    createUploadDownloadPaths ignoreSubdir

    local log="$HOME/.dbwebb-publish.log"
    local intro="Uploading the directory '$WHAT' to '$WHERE' to validate and publish."
    local command1="$RSYNC_CMD $OVERWRITE '$WHAT' '$WHERE'"
#    local command2="rsync -av $RSYNC_CHMOD $OVERWRITE --exclude .git --exclude .gitignore --exclude .default --exclude .solution --exclude .old --include='.??*' --exclude='*' -e \"ssh $DBW_SSH_KEY_OPTION\" '$DBW_COURSE_DIR/' '$DBW_REMOTE_DESTINATION/'"
    local command3="$SSH_CMD 'dbwebb-validate1 $dry $PUBLISH_OPTIONS --publish --course-repo \"$DBW_REMOTE_BASEDIR/$DBW_COURSE\" --publish-to \"$DBW_REMOTE_WWWDIR/$DBW_COURSE/$SUBDIR\" --publish-root \"$DBW_REMOTE_WWWDIR/$DBW_COURSE\" \"$DBW_REMOTE_BASEDIR/$DBW_COURSE/$SUBDIR\"' 2>&1 | tee '$log'; test \${PIPESTATUS[0]} -eq 0"
    local message="to validate and publish course results.\nSaved a log of the output: less -R '$log'"
    #executeCommand "$intro" "$command1; $command2; $command3" "$message"
    executeCommand "$intro" "$command1; $command3" "$message"

    if [ $? -eq 0 ]; then
        printf "Your files are now"
    else
        printf "Some of your files might be"
    fi
    printf " published on:\n $DBW_BASEURL/$DBW_COURSE/$SUBDIR\n"
}



#
# Publish the uploaded files without validation.
#
function dbwebb-fastpublish()
{
    PUBLISH_OPTIONS="--no-validate"
    dbwebb-publish "$1" "$2" "$3"
}

dbwebb-publishfast()
{
    dbwebb-fastpublish "$*"
}



#
# Publish the uploaded files
#
function dbwebb-purepublish()
{
    PUBLISH_OPTIONS="--no-validate --no-minification"
    dbwebb-publish "$1" "$2" "$3"
}

dbwebb-publishpure()
{
    dbwebb-purepublish "$*"
}



#
# Run command on remote server
#
function dbwebb-run()
{
    local cmd="$*"
    local cwd=
    
    # Prepare options
    if [[ ! $VERY_VERBOSE ]]; then
        SILENT="yes"
    fi

    if [[ $OPTION_CWD ]]; then
        cwd="cd $OPTION_CWD; "
    fi

    checkIfValidConfigOrExit
    
    # Create ssh-command for host
    local server=${OPTION_HOSTNAME:-$DBW_HOST}
    local ssh_cmd="ssh ${DBW_USER}@${server} $DBW_SSH_KEY_OPTION"
    
    local intro="I will now login to the server '$server' as '$DBW_USER' and execute the command '$cwd$cmd'."
    local command="$SSH_CMD -t \"$cwd$cmd\""
    local message="to run the command."
    
    executeCommand "$intro" "$command" "$message"
}



#
# Inspect uploaded files
#
function dbwebb-inspect()
{
    local course=
    local kmom=
    local who=
    local forWho=" for user '$DBW_USER'"
    local forCourse=
    local willUpload=
    local archive=
    local yes=
    local useVersion=
    local noValidate=

    checkIfValidConfigOrExit

    if [[ $3 ]]; then
        course="$1"
        forCourse=" in course '$course'"
        kmom="$2"
        who="$3"
        forWho=" for user '$who'"
        if [ "$who" != "$DBW_USER" -a -z "$OPTION_NOARCHIVE" ]; then
            archive="--archive $DBW_ARCHIVE"
        fi
    elif [[ $2 ]]; then
        course="$1"
        forCourse=" in course '$course'"
        kmom="$2"
    elif [[ $1 ]]; then
        course="$DBW_COURSE"
        kmom="$1"
        forCourse=" in course repo '$course'"
        willUpload=" I will start by uploading the course repo to the remote server."
    else
        usageInspect
        exit 0
    fi

    inspecUser=${who:=$DBW_USER}
    [[ $YES ]] && yes="--yes"
    [[ $PORT ]] && port="--port $PORT"
    [[ $USE_VERSION ]] && useVersion="--useVersion $USE_VERSION"
    [[ $NO_VALIDATE ]] && noValidate="--no-validate"

    local intro="I will now inspect '$kmom'${forCourse}${forWho}.$willUpload"
    local log="$HOME/.dbwebb-inspect.log"
    local command1=
    local command2="$SSH_CMD_INTERACTIVE \"dbwebb-inspect1 $yes $noValidate $port $useVersion $archive --publish-url $DBW_BASEURL --publish-to ~$DBW_USER/$DBW_REMOTE_WWWDIR --base-url $DBW_WWW_HOST~$inspecUser/$DBW_REMOTE_BASEDIR ~$inspecUser/$DBW_REMOTE_BASEDIR/$course $kmom\" 2>&1 | tee '$log'; test \${PIPESTATUS[0]} -eq 0"
    local message="to inspect the course results.\nSaved a log of the output, review it as:\nless -R '$log'"

    # Upload only if
    if [[ $willUpload ]]; then
        checkIfValidCourseRepoOrExit
        #setChmod
        command1="$RSYNC_CMD $OVERWRITE '$DBW_COURSE_DIR/' '$DBW_REMOTE_DESTINATION/';"
    fi

    executeCommand "$intro" "$command1 $command2" "$message"
}



#
# Re-create a lab
#
dbwebb-recreate()
{
    dbwebb-create "$1" "$2" "save-answers"
}



#
# Support re-create a lab by saving the lab answer
#
moveLabAnswer()
{
    local where="$1"
    local postfixFrom="$2"
    local postfixTo="$3"

    for extension in php js py bash
    do
        from="$where/answer.$extension$postfixFrom"
        to="$where/answer.$extension$postfixTo"
        if [ -f "$from" ]; then
            echo "(moving answer.$extension$postfixFrom to answer.$extension$postfixTo)"
            mv "$from" "$to"
        fi
    done
}



#
# Create a lab
#
dbwebb-create()
{
    local myWget=
    local lab="$1"
    local version="$2"
    local save="$3"
    local subdir="$( mapCmdToDir $lab )"
    local where="$DBW_COURSE_DIR/$subdir"
    
    if [ -z "$subdir" ]; then
        printf "$MSG_FAILED Not a valid combination of '$DBW_COURSE' and '$lab'.\n"
        exit 2
    fi

    checkIfValidConfigOrExit
    checkIfValidCourseRepoOrExit

    # If no lab version supplied, read it from course repo if available
    if [ -z "$version" -a -f "$DBW_COURSE_DIR/.dbwebb/lab.version" ]; then
        version=$( cat "$DBW_COURSE_DIR/.dbwebb/lab.version" )
    fi

    local lab_version=
    [[ $version ]] && lab_version=" ($version)"

    printf "Creating $DBW_COURSE $lab${lab_version} in '$where'.\n"

    # Check if init was run?
    if [ ! -d "$where" ]; then
        printf "$MSG_FAILED The directory '$where' is missing.\nDid you run the command 'dbwebb init'?\n"
        exit 2
    fi

    # Check if lab is already there
    if [ -f "$where/answer.php" -o -f "$where/answer.js" -o -f "$where/answer.py" -o -f "$where/answer.bash" ]; then
        if [[ $save ]]; then
            moveLabAnswer "$where" "" "_$$"
        else
            printf "$MSG_FAILED You have already created lab-files at: '$where'\nRemove the files in the directory, then you can generate new files.\n"
            exit 2
        fi
    fi

    # Check for wget or curl
    myWget="wget -qO"
    #myWget="curl -so"

    # Get the lab bundle
    #http://localhost/git/lab/?action=bundle&key=a07e843ca67d647900e8259729119ddf
    #http://localhost/git/lab/?action=bundle&course=linux&lab=lab1&acronym=mos&doGenerate
    local bundleQuery="?action=bundle&acronym=$DBW_USER&course=$DBW_COURSE&lab=$lab&version=$version&doGenerate=Submit"

    printf "Downloading and extracting lab bundle\n"
    [[ $VERY_VERBOSE ]] && echo " ($DBW_LABURL/$bundleQuery)"
    [[ $VERY_VERBOSE ]] && echo " ($myWget $where/bundle.tar '$DBW_LABURL/$bundleQuery')"

    $myWget "$where/bundle.tar" "$DBW_LABURL/$bundleQuery"
    [[ $? > 0 ]] && printf "$MSG_FAILED Failed to download lab bundle.\n" && exit 1

    tar -xmf "$where/bundle.tar" -C "$where"
    [[ $? > 0 ]] && printf "$MSG_FAILED The downloaded lab bundle is not a tar archive. You may cat it to check for errors.\n" && exit 1

    rm -f "$where/bundle.tar"
    if [[ $save ]]; then
        moveLabAnswer "$where" "_$$" ""
    fi
    printf "$MSG_DONE You can find the lab and all files here:\n"
    echo "'$where'"
    ls -lF "$where"
}



#
# Selfupdate
#
dbwebb-selfupdate()
{
    local version="$1"
    selfupdate dbwebb "$version"
    dbwebb updateconfig
}



#
# Clone a repo
#
dbwebb-clone()
{
    local repo="$1"
    local saveas="$2"
    
    if [[ ! $repo ]]; then
        usageClone
        exit 0
    fi
    
    if ! contains "$repo" "${DBW_COURSE_REPOS[@]}"; then
        badUsageClone "$MSG_FAILED Not a valid course repo: '$repo'"
        exit 1
    fi
    
    local intro="Cloning course-repo for '$repo' from '$( createGithubUrl "$repo" )'."
    local cmd="git clone \"$( createGithubUrl "$repo" ).git\" $saveas"
    local message="to clone course repo."
    executeCommand "$intro" "$cmd" "$message"
}



#
# Link to a repo on GitHub
#
dbwebb-github()
{
    local repo="$1"
    
    if [[ ! $repo ]]; then
        usageGithub
        exit 0
    fi
    
    if ! contains "$repo" "${DBW_COURSE_REPOS[@]}"; then
        badUsageGithub "$MSG_FAILED Not a valid course repo: '$repo'"
        exit 1
    fi
    
    echo "The course repo '$repo' exists on GitHub:"
    echo "Repo:   $( createGithubUrl "$repo" )"
    echo "Issues: $( createGithubUrl "$repo" "/issues" )"
}



#
# Update config during selfupdate
#
dbwebb-updateconfig()
{
    createConfig "selfupdate" "no"
}



#
# Support testing procedure of course repo
#
dbwebb-testrepo()
{
    checkIfValidConfigOrExit
    checkIfValidCourseRepoOrExit

    local base="$DBW_COURSE_DIR"
    local me="$base/me"
    local testsuite="$base/.dbwebb.tests"
    local prompt="==="
    local silent=

    if [ ! -f "$testsuite" ]; then
        printf "$MSG_FAILED There is no testsuite file '.dbwebb.tests' in this course repo. This courese repo does not (yet) support automated tests.\n"
        exit 2
    fi

    if [ -d "$me" -a -z "$OVERWRITE" ]; then
        printf "$MSG_FAILED The me-directory is already here. You can not run tests with an existing me-directory. Remove the me-directory so it can be recreated by the test procedure.\n"
        exit 2
    fi

    if [[ $SILENT ]]; then
        silent=" &> /dev/null"
    fi

    echo "$prompt Beginning testsuite"
    if [[ $OPTION_LOCAL ]]; then
        echo "$prompt Test locally, using dbwebb-validate"
    fi
    cd "$base"

    local lineNum=0
    local assertions=0
    local fail=0
    
    while IFS= read -r line <&3
    do
        ((lineNum++))
        printf "%s %s: %s\n" "$prompt" "$lineNum" "$line"

        if [ -z "$line" -o "${line:0:1}" == "#" ]; then
            continue;
        fi

        if [[ $OPTION_LOCAL ]]; then
            line=$( echo "$line" | sed 's/dbwebb validate /make dbwebb-validate what=/')
        fi

        if [[ $SILENT ]]; then
            line=$( echo "$line" | sed 's/;/ \&> \/dev\/null;/' )
        fi

        if [[ $VERY_VERBOSE ]]; then
            printf "$prompt $lineNum: $line $silent\n"
        fi

        ((assertions++))
        ( bash -c "$line $silent 3<&-" )
        if [ $? != 0 ]; then
            printf "$prompt $lineNum: $MSG_FAILED\n"
            ((fail++))
        fi

    done 3< <(grep "" "$testsuite")

    echo "$prompt Done with $assertions assertions and $fail failure(s)."
    if [[ $fail = 0 ]]; then
        printf "$prompt $MSG_OK All tests passed.\n"
    else 
        printf "$prompt $MSG_FAILED $fail assertion(s) failed.\n"
        exit 1
    fi
}



# --------------- DBWEBB MAIN START HERE ------------------------------
#
# Process options
#
while (( $# ))
do
    case "$1" in
        
        --inspect | -i)
            inspectUsage
            exit 0
        ;;

        --verbose | -v)
            VERY_VERBOSE="yes"
            shift
        ;;

        --silent | -s)
            SILENT="yes"
            shift
        ;;

        --yes | -y)
            YES="yes"
            shift
        ;;

        --port | -p)
            PORT="$2"
            shift
            shift
        ;;

        --useVersion | -u)
            USE_VERSION="$2"
            shift
            shift
        ;;

        --no-archive)
            OPTION_NOARCHIVE="yes"
            shift
        ;;

        --no-validate)
            NO_VALIDATE="yes"
            shift
        ;;

        --local | -l)
            OPTION_LOCAL="yes"
            shift
        ;;

        --dry)
            OPTION_DRY="yes"
            shift
        ;;

        --force | -f)
            OVERWRITE="--ignore-times"
            shift
        ;;

        --host | --hostname)
            OPTION_HOSTNAME="$2"
            shift
            shift
        ;;

        --cwd)
            OPTION_CWD="$2"
            shift
            shift
        ;;

        --help | -h)
            usage
            exit 0
        ;;
        
        --version)
            version
            exit 0
        ;;
                
        #--course)
        #    DBW_COURSE=$2
        #    shift
        #    shift
        #;;
                
        update         \
        | check        \
        | clone        \
        | github       \
        | config       \
        | updateconfig \
        | selfupdate   \
        | sshkey       \
        | login        \
        | upload       \
        | download     \
        | validate     \
        | publish      \
        | publishfast  \
        | publishpure  \
        | fastpublish  \
        | purepublish  \
        | inspect      \
        | run          \
        | create       \
        | recreate     \
        | testrepo     \
        | init         \
        | init-server  \
        | init-structure-dbwebb-kurser \
        | init-structure-www-dbwebb-kurser \
        | init-me)
            command=$1
            shift
            dbwebb-$command $*
            exit
        ;;
        
        *)
            badUsage "$MSG_FAILED Option/command not recognized."
            exit 2
        ;;
        
    esac
done

badUsage
exit 1
