#!/bin/sh

#SCRIPT=$(readlink -f "$0")
#SCRIPTPATH=$(dirname "$SCRIPT")
#. "$SCRIPTPATH/.dbwebb.version"
. "bin/.dbwebb.version"

TESTS=0
ASSERTS=0
FAULTS=0

ECHO="printf"
TMPFILE="/tmp/dbwebb-error$$"


#
# External tools
#
JSHINT="jshint"

HTMLHINT="htmlhint"

# Change this to csshint?
CSSHINT="htmlhint"

HTML_MINIFIER="html-minifier"
HTML_MINIFIER_OPTIONS="--minify-js --minify-css --use-short-doctype --remove-attribute-quotes --collapse-boolean-attributes --conservative-collapse --collapse-whitespace --remove-comments"

UGLIFYJS="uglifyjs"
UGLIFYJS_OPTIONS="--mangle --compress --screw-ie8 --comments"

CLEANCSS="cleancss --s1"

PYLINT="pylint -r n --rcfile ./.pylintrc"



#
# Settings for this script
#
umask 022
export NODE_PATH=/usr/local/lib/node_modules


#
# Print usage of script
#
printUsage()
{
    $ECHO "\nUtility dbwebb-validate $VERSION by Mikael Roos, to validate and publish course results when done."
    $ECHO "\n"
    $ECHO "\nUsage: dbwebb-validate [options] <item>"
    $ECHO "\n"
    $ECHO "Options:"
    $ECHO "\n"
    $ECHO "\n  -h         Print this message and exit."
    $ECHO "\n  -i         Ignore validation faults and proceed anyway."
    $ECHO "\n  -n         Only validate, do not publish."
    $ECHO "\n  -e         Do not check for installed tools.."
    $ECHO "\n  -m         Do not chmod."
    $ECHO "\n  -t         Do not test me-structure."
    $ECHO "\n  -r         Print required external commands."
    #$ECHO "\n  -u user    Validate a specific user."
    $ECHO "\n  -s         Silent, limited output."
    $ECHO "\n  -v         Print version and exit."
    $ECHO "\n"
    $ECHO "\nItem:"
    $ECHO "\n"
    $ECHO "\n  kmom01    Validate (and publish) selected kmom."
    $ECHO "\n  kmom02    Validate (and publish) selected kmom."
    $ECHO "\n  kmom03    Validate (and publish) selected kmom."
    $ECHO "\n  kmom04    Validate (and publish) selected kmom."
    $ECHO "\n  kmom05    Validate (and publish) selected kmom."
    $ECHO "\n  kmom06    Validate (and publish) selected kmom."
    $ECHO "\n  kmom10    Validate (and publish) selected kmom."
    $ECHO "\n  me        Validate (and publish) all kmoms."
    $ECHO "\n  tutorial  Validate (and publish) the tutorial."
    $ECHO "\n  example   Validate (and publish) the example programs."
    $ECHO "\n  all       Validate (and publish) me, tutorial and example."
    $ECHO "\n"
    $ECHO "\nExample:"
    $ECHO "\n"
    $ECHO "\n  $ dbwebb-validate kmom01"
    $ECHO "\n  $ dbwebb-validate -n kmom01"
    $ECHO "\n  $ dbwebb-validate me"
    $ECHO "\n"
    $ECHO "\n"
}



#
# Map command to path
#
mapCmdToDir()
{
    CMD="$1"
    RES=""

    case "$CMD" in
        example)    RES="example" ;;
        tutorial)   RES="tutorial" ;;
        me)         RES="me" ;;
        all)        RES="all" ;;
        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

    if [ ! -z $RES ]; then 
        $ECHO "$RES"
        return
    fi 

    case "$COURSE" in
        javascript1)
            case "$CMD" in
                sandbox)    RES="me/kmom01/sandbox" ;;
                hangman)    RES="me/kmom06/hangman" ;;

                lab1)       RES="me/kmom02/lab1" ;;
                lab2)       RES="me/kmom03/lab2" ;;
                lab3)       RES="me/kmom04/lab3" ;;
                lab4)       RES="me/kmom04/lab4" ;;
                lab5)       RES="me/kmom05/lab5" ;;

                flag1)      RES="me/kmom02/flag1" ;;
                flag2)      RES="me/kmom03/flag2" ;;
                flag3)      RES="me/kmom04/flag3" ;;
                flag4)      RES="me/kmom05/flag4" ;;
                flag5)      RES="me/kmom06/flag5" ;;

                baddie1)    RES="me/kmom02/baddie1" ;;
                baddie2)    RES="me/kmom03/baddie2" ;;
                baddie3)    RES="me/kmom04/baddie3" ;;
                #baddie4)    RES="me/kmom05/baddie4" ;;
                #baddie5)    RES="me/kmom06/baddie5" ;;
            esac
            ;;
        python)
            case "$CMD" in
                hello)      RES="me/kmom01/hello" ;;
                plane)      RES="me/kmom01/plane" ;;

                lab1)       RES="me/kmom02/lab1" ;;
                lab2)       RES="me/kmom03/lab2" ;;
                lab3)       RES="me/kmom04/lab3" ;;
                lab4)       RES="me/kmom05/lab4" ;;
                lab5)       RES="me/kmom06/lab5" ;;
                lab6)       RES="me/kmom06/lab6" ;;

                marvin1)    RES="me/kmom02/marvin1" ;;
                marvin2)    RES="me/kmom03/marvin2" ;;
                marvin3)    RES="me/kmom04/marvin3" ;;
                marvin4)    RES="me/kmom05/marvin4" ;;
                marvin5)    RES="me/kmom06/marvin5" ;;

                game1)      RES="me/kmom04/game1" ;;
                game2)      RES="me/kmom05/game2" ;;
                game3)      RES="me/kmom06/game3" ;;
            esac
            ;;
        *)
            $ECHO "\n$MSG_FAILED Unknown Course. Error in config file.\n"
            exit 1
            ;;
    esac

    if [ ! -z "$RES" ]; then 
        $ECHO "$RES"
        return
    fi 

    $ECHO "\n$MSG_FAILED Unknown combination of Course and Item: $COURSE $CMD.\n"
    exit 1
}



#
# Test me/ to contain at least expected items
#
testMe()
{
    TESTS=$(( $TESTS + 1 ))
    CMD="$1"
    DIRS=""
    FILES=""
    ME="$TARGET/me"

    case $COURSE in

        javascript1) 
            FILES="me.html redovisning.html" 
            KMOM01="kmom01 kmom01/sandbox"
            KMOM02="kmom02 kmom02/flag1 kmom02/lab1 kmom02/baddie1"
            KMOM03="kmom03 kmom03/flag2 kmom03/lab2 kmom03/baddie2"
            KMOM04="kmom04 kmom04/flag3 kmom04/lab3 kmom04/lab4 kmom04/baddie3"
            KMOM05="kmom05 kmom05/flag4 kmom05/lab5"
            KMOM06="kmom06 kmom06/hangman"
            KMOM10="kmom10"            
            ;;

        python)      
            FILES="me.cgi me.py redovisning.cgi redovisning.py" 
            KMOM01="kmom01 kmom01/hello kmom01/plane"
            KMOM02="kmom02 kmom02/marvin1 kmom02/lab1"
            KMOM03="kmom03 kmom03/marvin2 kmom03/lab2"
            KMOM04="kmom04 kmom04/marvin3 kmom04/lab3 kmom04/game1"
            KMOM05="kmom05 kmom05/marvin4 kmom05/lab4 kmom05/game2"
            KMOM06="kmom06 kmom06/marvin5 kmom06/lab5 kmom06/game3"
            KMOM10="kmom10"
            ;;

        *)           
            FILES="" 
            ;;
    esac

    case "$CMD" in
        kmom01)     DIRS="$KMOM01" ;;
        kmom02)     DIRS="$KMOM02" ;;
        kmom03)     DIRS="$KMOM03" ;;
        kmom04)     DIRS="$KMOM04" ;;
        kmom05)     DIRS="$KMOM05" ;;
        kmom06)     DIRS="$KMOM06" ;;
        kmom10)     DIRS="$KMOM10" ;;
        me)         DIRS="$KMOM01 $KMOM02 $KMOM03 $KMOM04 $KMOM05 $KMOM06 $KMOM10" ;;
        all)        DIRS="$KMOM01 $KMOM02 $KMOM03 $KMOM04 $KMOM05 $KMOM06 $KMOM10" ;;
        *)          DIRS=""; FILES="" ;;
    esac

    if [ -z "$DIRS$FILES" ]; then
        return
    fi

    $ECHO "\n\nTesting Me-directory items."

    for dir in $DIRS; do
        #$ECHO "\n $dir"
        assert 0 "[ -d $ME/$dir ]"  "Missing directory: me/$dir"
    done

    for file in $FILES; do
        #$ECHO "\n $file"
        assert 0 "[ -f $ME/$file ]" "Missing file: me/$file" 
    done
}



#
# Perform an assert
#
assert()
{
    EXPECTED=$1
    TEST=$2
    MESSAGE=$3
    ASSERTS=$(( $ASSERTS + 1 ))

    sh -c "$TEST" > "$TMPFILE" 2>&1
    STATUS=$?
    ERROR=$(cat $TMPFILE)

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

        case $IGNORE_FAULTS in
            yes) MSG="\n\n$MSG_WARNING $MESSAGE\n" ;;
            no)  MSG="\n\n$MSG_FAILED $MESSAGE\n" ;;
        esac

        $ECHO "$MSG" 
        [ -z "$ERROR" ] || $ECHO "$ERROR\n\n"

        if [ $IGNORE_FAULTS = "no" ]; then 
            $ECHO "\nTests: $TESTS Asserts: $ASSERTS Faults: $FAULTS\n"
            $ECHO "\nCorrect the fault and run the validation again.\n\n"
            exit 1
        fi
    fi

    return $STATUS
}



#
# Check for installed commands
#
checkCommand()
{
    COMMAND=$1
    #$ECHO "\nChecking $COMMAND"

    if ! hash "$COMMAND" 2>/dev/null; then
        $ECHO "\n I require $COMMAND but it's not installed. Ignoring."
        #$ECHO >&2 "\nI require $COMMAND but it's not installed. Ignoring for now."
        #$ECHO >&2 "\nUse the following command to see what is required to run this script"
        #$ECHO >&2 "\n  dbwebb-validate -r";
        #$ECHO >&2 "\n";
        #exit 1
    fi
}



#
# Check for installed tools
#
CHECKTOOLS="no"
checkTools()
{
    [ $CHECKTOOLS = "no" ] || return; CHECKTOOLS="yes"

    $ECHO "\nCheck for installed tools."

    checkCommand $PYLINT

    checkCommand $HTMLHINT
    checkCommand $CSSHINT
    checkCommand $JSHINT

    [ $PUBLISH = "yes" -o $IGNORE_FAULTS = "yes" ] || return

    checkCommand $HTML_MINIFIER
    checkCommand $UGLIFYJS
    checkCommand $CLEANCSS
}



#
# Set proper rights for files and directories
#
setChmod()
{
    $ECHO "\nUpdating chmod on file and dir."

    for filename in $(find "$TARGET" -type d); do
        chmod 755 "$filename"
    done

    chmod -R u+rw "$TARGET" 
    chmod -R go+r "$TARGET"
}



#
# Perform validation tests
#
validateHtmlCssJs()
{
    TESTS=$(( $TESTS + 1 ))
    DIR="$1"
    SILENT="$2"

    if [ -z "$SILENT" ]; then $ECHO "\nValidate $DIR."; fi

    assert 0 "[ -d $THETARGET/$DIR ]" "Missing directory: $THETARGET/$DIR" || return

    $ECHO "\n *.html using HTMLHint."
    #find $TARGET/$DIR -type f -name '*.html' | while read filename; do assert 0 "$HTMLHINT $filename" "HTMLHINT failed: $filename"; done
    for filename in $(find "$THETARGET/$DIR/" -type f -name '*.html'); do
        assert 0 "$HTMLHINT $filename" "HTMLHINT failed: $filename"
    done

    #$ECHO "\n *.css using HTMLHint."
    #find $TARGET/$DIR -type f -name '*.css' | while read filename; do assert 0 "$HTMLHINT $filename" "HTMLHINT failed: $filename"; done

    $ECHO "\n *.js using JSHint."
    #find $TARGET/$DIR -type f -name '*.js' | while read filename; do assert 0 "$JSHINT $filename" "JSHint failed: $filename"; done
    for filename in $(find "$THETARGET/$DIR/" -type f -name '*.js'); do
        assert 0 "$JSHINT $filename" "JSHint failed: $filename"
    done
}



#
# Perform validation tests
#
validatePython()
{
    TESTS=$(( $TESTS + 1 ))
    DIR="$1"
    SILENT="$2"

    if [ -z "$SILENT" ]; then $ECHO "\nValidate $DIR."; fi
    
    assert 0 "[ -d $THETARGET/$DIR ]" "Missing directory: $THETARGET/$DIR" || return

    $ECHO "\n *.py using Pylint."
    for filename in $(find "$THETARGET/$DIR/" -type f -name '*.py'); do
        assert 0 "$PYLINT $filename" "pylint failed: $filename"
    done

    $ECHO "\n *.cgi using Pylint."
    for filename in $(find "$THETARGET/$DIR/" -type f -name '*.cgi'); do
        assert 0 "$PYLINT $filename" "pylint failed: $filename"
    done
}



#
# Validete by all techniques
#
validate()
{
    TESTS=$(( $TESTS + 1 ))
    DIR="$1"

    if [ "$DIR" = "all" ]; then
        DIR="example tutorial me"
    fi

    $ECHO "\n\nValidate $DIR."

    # Validate for another user
    THETARGET="$TARGET"
    if [ ! -z "$THEUSER" ]
    then
        THETARGET=`eval echo "~$THEUSER/dbwebb-kurser/$COURSE"`
    fi

    for dir in $DIR; do
        assert 0 "[ -d $THETARGET/$dir ]" "Missing directory: $THETARGET/$dir" || return
        validateHtmlCssJs "$dir" "silent" "$THETARGET"
        validatePython "$dir" "silent" "$THETARGET"
    done
}



#
# Init the build directory
#
initBuildDir()
{
    SILENT="$1"

    if [ -z "$SILENT" ]; then
        $ECHO "\nChecking the www directory."
    fi

    ME="$BUILD/me"
    for dir in $BUILD1 $BUILD2 $BUILD3 $BUILD $ME $ME/kmom01 $ME/kmom02 $ME/kmom03 $ME/kmom04 $ME/kmom05 $ME/kmom06 $ME/kmom10; do
        if [ ! -d $dir ]; then mkdir $dir; fi
    done
}



#
# Publish all
#
publishChmod()
{
    DIR="$1"

    #find $BUILD/$dir/ -type d -print0 | xargs -0 chmod 755 
    #find $BUILD/$dir/ -type f -print0 | xargs -0 chmod 644
    #find $BUILD/$dir/ -type f -name '*.cgi' -print0 | xargs -0 chmod 755
    #find $BUILD/$dir/ -type f -name '*.py'  -print0 | xargs -0 chmod 600

    for filename in $(find "$DIR" -type d); do
        chmod 755 "$filename"
    done

    for filename in $(find "$DIR" -type f); do
        chmod 644 "$filename"
    done

    for filename in $(find "$DIR" -type f -name '*.cgi'); do
        chmod 755 "$filename"
    done

    for filename in $(find "$DIR" -type f -name '*.py'); do
        chmod 600 "$filename"
    done
}



#
# Publish all
#
publish()
{
    DIR="$1"
    SILENT="$2"

    if [ -z "$SILENT" ]; then
        $ECHO "\n\nPublishing $DIR to www/$BASEDIR/$PROJECT."
    fi
   
    case "$DIR" in
        example)    DIRS="example" ;;
        tutorial)   DIRS="tutorial" ;;
        me)         DIRS="me" ;;
        all)        DIRS="example tutorial me" ;;
        kmom*)      DIRS="me/js me/style $DIR" ;;
        *)          DIRS="$DIR" ;;
    esac

    rsync -a --delete --exclude 'me/default' -f '- /*/' $TARGET/me/ $BUILD/me/
    
    publishChmod "$BUILD/me/"

    for filename in $(find $BUILD/me/ -maxdepth 1 -type f -name '*.html'); do
        assert 0 "$HTML_MINIFIER $HTML_MINIFIER_OPTIONS $filename --output $filename" "HTMLMinifier failed: $filename"
    done

    for filename in $(find $BUILD/me/ -maxdepth 1 -type f -name '*.js'); do
        assert 0 "$UGLIFYJS $filename -o $filename $UGLIFYJS_OPTIONS" "UglifyJS failed: $filename"
    done

    for filename in $(find $BUILD/me/ -maxdepth 1 -type f -name '*.css'); do
        assert 0 "$CLEANCSS $filename -o $filename" "CleanCSS failed: $filename"
    done

    #find "$BUILD/me/" -maxdepth 1 -type f -name '*.html' | while read filename; do $HTML_MINIFIER $HTML_MINIFIER_OPTIONS $filename --output $filename; exit; done
    #find "$BUILD/me/" -maxdepth 1 -type f -name '*.js'   | while read filename; do $UGLIFYJS $filename -o $filename $UGLIFYJS_OPTIONS; done
    #find "$BUILD/me/" -maxdepth 1 -type f -name '*.css'  | while read filename; do $CLEANCSS $filename -o $filename; done

    for dir in $DIRS; do
        if [ ! -d "$TARGET/$dir" ]; then continue; fi

        $ECHO "\n $dir:"
        rsync -a --delete $TARGET/$dir/ $BUILD/$dir/ 
        
        publishChmod "$BUILD/$dir/"

        if [ -z "$SILENT" ]; then $ECHO " minify *.html"; fi
        #find "$BUILD/$dir/" -type f -name '*.html' | while read filename; do assert "0" "$HTML_MINIFIER $HTML_MINIFIER_OPTIONS $filename --output $filename" "HTMLMinifier failed: $filename"; done
        for filename in $(find "$BUILD/$dir/" -type f -name '*.html'); do
            assert 0 "$HTML_MINIFIER $HTML_MINIFIER_OPTIONS $filename --output $filename" "HTMLMinifier failed: $filename"
        done

        if [ -z "$SILENT" ]; then $ECHO " uglify *.js"; fi
        #$UGLIFYJS $BUILD/$KMOM/$DIR/js/main.js -o $BUILD/$KMOM/$DIR/js/main.js $UGLIFYJS_OPTIONS
        #find "$BUILD/$dir/" -type f -name '*.js' | while read filename; do assert "0" "$UGLIFYJS $filename -o $filename $UGLIFYJS_OPTIONS" "UglifyJS failed: $filename"; done
        for filename in $(find "$BUILD/$dir/" -type f -name '*.js'); do
            assert 0 "$UGLIFYJS $filename -o $filename $UGLIFYJS_OPTIONS" "UglifyJS failed: $filename"
        done

        
        if [ -z "$SILENT" ]; then $ECHO " minify *.css"; fi
        #$CLEANCSS $BUILD/$KMOM/$DIR/style/style.css -o $BUILD/$KMOM/$DIR/style/style.css
        #find "$BUILD/$dir/" -type f -name '*.css' | while read filename; do assert "0" "$CLEANCSS $filename -o $filename" "CleanCSS failed: $filename"; done
        for filename in $(find "$BUILD/$dir/" -type f -name '*.css'); do
            assert 0 "$CLEANCSS $filename -o $filename" "CleanCSS failed: $filename"
        done

    done
}



#
# Main
#
if [ $# != 3 ]
then
    :
    #$ECHO
    #printUsage
fi

# Get settings from config-file
. "./.dbwebb.config"


# Get options
OPT_CHMOD="yes"
OPT_CHECK="yes"
OPT_ME="yes"
SILENT="no"
PUBLISH="yes"
IGNORE_FAULTS="no"

while getopts ":hinrvmesut" opt
do
    case $opt in
        (h) 
            printUsage
            exit 0
            ;;

        (i) 
            IGNORE_FAULTS="yes"
            ;;

        (n) 
            PUBLISH="no"
            ;;

        (m) 
            OPT_CHMOD="no"
            ;;

        (e) 
            OPT_CHECK="no"
            ;;

        (t) 
            OPT_ME="no"
            ;;

        #(u) 
        #    THEUSER="$OPTARG"
        #    shift
        #    ;;

        (r) 
            $ECHO "\nRequired external tools are:"
            $ECHO "\n  npm install jshint html-minifier uglify-js clean-css htmlhint pylint"
            $ECHO "\n"
            $ECHO "\n"
            exit 0
            ;;

        (s) 
            SILENT="yes"
            ;;

        (v) 
            $ECHO "$VERSION\n"
            exit 0
            ;;

        \?)
            $ECHO "\nInvalid option: -$OPTARG" >&2
            $ECHO "\n"
            $ECHO "\nUse the following to get help."
            $ECHO "\ndbwebb-validate -h"
            $ECHO "\n"
            $ECHO "\n"
            exit 1
            ;;
    esac
done
shift $((OPTIND - 1))


# Get path to dir to check
FAULTS=0
CMD=$1
THEUSER=$2
DIR=$(mapCmdToDir "$CMD") 
if [ $? -eq 1 ]; then
    $ECHO "$DIR"
    exit 1
fi


# Execute command
if [ $OPT_CHECK = "yes" ]; then checkTools; fi
if [ $OPT_CHMOD = "yes" ]; then setChmod; fi
if [ $OPT_CHMOD = "yes" ]; then testMe "$CMD"; fi

validate "$DIR" 

if [ $PUBLISH = "yes" ]; then
    initBuildDir "silent"
    publish "$DIR"
fi


# Clean up and output results
rm -f $TMPFILE

if [ $FAULTS -gt 0 ]
then
        if [ $IGNORE_FAULTS = "yes" ]
        then
            $ECHO "\n\n$MSG_WARNING"
            STATUS=2
        else
            $ECHO "\n\n$MSG_FAILED"
            STATUS=1
        fi
        $ECHO " Tests: $TESTS Asserts: $ASSERTS Faults: $FAULTS\n"
        exit $STATUS
fi

$ECHO "\n\n$MSG_OK"
$ECHO " Tests: $TESTS Asserts: $ASSERTS Faults: $FAULTS\n"
exit 0
