#!/bin/bash

self=$0
command=$1
shift

# allow normal shell commands to be executed (eg: npm install)
# # when the script is used as an ENTRYPOINT for a docker container
if [ "$command" = "exec" ]; then
    exec "$@"
fi

#################################################################################
# environment checks for parallel and jq
# TODO: don't check on *every* run...that's a lot of checks
which parallel > /dev/null
if [ $? -gt 0 ]; then
    echo "!! parallel not found.  installing..."
    brew install parallel
    mkdir -p ~/.parallel
    touch ~/.parallel/will-cite
fi

which jq > /dev/null
if [ $? -gt 0 ]; then
    echo "!! jq not found.  installing..."
    brew install jq
fi

#################################################################################
# configuration (export because `parallel`)
export TAPE_CMD=node_modules/tape/bin/tape
export KARMA_CMD=node_modules/karma/bin/karma
export BROWSERIFY_CMD=node_modules/browserify/bin/cmd.js
export EXORCIST_CMD=node_modules/exorcist/bin/exorcist.js
export WATCHIFY_CMD=node_modules/watchify/bin/cmd.js
export WATCH_CMD=node_modules/chokidar-cli/index.js
export SASS_CMD=node_modules/node-sass/bin/node-sass
PARALLEL_CMD='parallel --line-buffer --halt 2'

# read some names out of the package.json
export ENTRY_POINT=$(jq -r .main package.json);
export DEFAULT_NAME=$(jq -r .name package.json);

# configuration settings
export FEAT_APP=$(jq -r .iqproj.app package.json)

#################################################################################
# utilities
function find-next-open-port() {
    for port in $(seq $1 $(($1 + 1000))); do
        echo "\035" | /usr/bin/nc 127.0.0.1 $port > /dev/null
        if [ $? -gt 0 ]; then 
            echo $port
            return 0
        fi
    done
    return 1
}

#################################################################################
# informational messages
echo "# RUN $command $@"

# essential prep
# iqb outputs all build artifacts to the release/ folder
mkdir -p release/

# super basic override functionality.  allow shipping with a base task,
# but allow that task to be overridden by creating a file in build/commandname
# BONUS: also lets you add new commands (written in any language!)
if [ -f "build/$command" ]; then
    echo "** Override: $command (build/$command)"
    exec build/$command "$@"
fi

#################################################################################
# Built-in [standard] commands
# Any of these can be overridden by creating a `build/<taskname>` file
START_TIME=$(node -e "console.log(+new Date())")

set -e
case $command in
    # npm lifecycle commands
    "test")
        if [ "$FEAT_APP" = "true" ]; then
            $KARMA_CMD start --single-run --no-auto-watch karma.conf.js 
        else
            $TAPE_CMD ${1:-src/**/*.spec.js}
        fi
        ;;

    # basic housekeeping tasks
    "clean")      rm -rf release/* ;;
    "assets")     [ -d src/assets ] && cp -R src/assets release ;;

    # testing
    "watch:test") 
        if [ "$FEAT_APP" = "true" ]; then
            $KARMA_CMD start karma.conf.js 
        else
            $self test && $WATCH_CMD --silent ${1-src/**/*.spec.js} -c "$self test" 
        fi
        ;;

    # browserify feature
    "watch:js")   $WATCHIFY_CMD $ENTRY_POINT -d -o "$EXORCIST_CMD release/${DEFAULT_NAME}.js.map > release/${DEFAULT_NAME}.js" ;;
    "build:js")   $BROWSERIFY_CMD $ENTRY_POINT -d | $EXORCIST_CMD release/${DEFAULT_NAME}.js.map > release/${DEFAULT_NAME}.js ;;

    # scss feature
    "watch:scss") $self build:scss && $WATCH_CMD --silent 'src/**/*.scss' -c "$self build:scss" ;;
    "build:scss")
        [ -f src/scss/${DEFAULT_NAME}.scss ] && ($SASS_CMD src/scss/${DEFAULT_NAME}.scss > release/${DEFAULT_NAME}.css || exit 1);
        [ -f src/scss/app.scss ] && ($SASS_CMD src/scss/app.scss > release/app.css || exit 1);
        ;;

    # app feature
    "watch:html") $self build:html && $WATCH_CMD --silent app/* -c "$self build:html" ;;
    "build:html") cp app/index.html release/ ;;
    "watch:app")  $self build:app && $WATCHIFY_CMD app/index.js -d -o "$EXORCIST_CMD release/app.js.map > release/app.js" ;;
    "build:app")  $BROWSERIFY_CMD app/index.js -d | $EXORCIST_CMD release/app.js.map > release/app.js ;;
    "livereload") $WATCH_CMD --silent release/* -c "curl -s http://localhost:${1:-35729}/changed\?files\=index.html" ;;
    "serve")
        SRV_PORT=$(find-next-open-port 8080)
        LR_PORT=$(find-next-open-port 35729)
        $PARALLEL_CMD ::: "$self start-server $SRV_PORT $LR_PORT" "$self livereload $LR_PORT"
        ;;

    # entry points
    "start"|"watch")
        features=("$self assets" "$self watch:test")

        if [ "$FEAT_APP" = "true" ]; then
            features+=(
                "$self watch:app"
                "$self watch:html"
                "$self watch:scss"
                "$self watch:js"
                "$self serve"
            );
        fi

        $PARALLEL_CMD ::: "${features[@]}"
        ;;

    "build")
        features=("$self assets" "$self test")

        if [ "$FEAT_APP" = "true" ]; then
            features+=(
                "$self build:scss"
                "$self build:js"
                "$self build:app"
                "$self build:html"
            );
        fi

        $self clean &&
        $PARALLEL_CMD ::: "${features[@]}"
        ;;

    *)
        echo "Unknown command: $command"
        exit 1
esac

END_TIME=$(node -e "console.log(+new Date())")
ELAPSED=$(( $END_TIME - $START_TIME ))
TIMESTR=$(node -e "console.log($ELAPSED < 1000 ? $ELAPSED + 'ms' : ($ELAPSED / 1000.0) + 's')");

echo "---DONE [ $TIMESTR ] $command $*"

