#!/bin/sh

########################################################## 
#               Butterfly Control System                 #
##########################################################

# -------------- utility functions ----------------------

fail () {
   cat <<EOF
ERROR: $1
Type '$0 -h' for usage information.
EOF
exit 1
}

error() {
    echo "$1"
    exit 1
}

warn() {
    echo "$1"
    exit 0
}

usage() {
    cat <<EOF
Usage: $0 [options] <action>
where [options] include:

  -h print this message and exit
  
  -p <port> the port that the server will listen to
     default: 8080

  -i <interface> the host interface the server will bind to
     default: 127.0.0.1

  -w <path> path to the webapp
     default: main/webapp

  -m <memory> max memory heap size to use
     default: 1024M
               
  --debug enable JVM debugging (on port 8000)
       
  --jmx enable JMX monitoring (for jconsole and jvisualvm)
  
and <action> is one of

   build ..................... Build Butterfly      
   run ....................... Run Butterfly [default]

   appengine_build ........... Build Butterfly webapp for Google App Engine
   appengine_run ............. Run Butterfly webapp for Google App Engine in local server
   appengine_upload .......... Upload Butterfly webapp to Google App Engine
   
   test ...................... Run Butterfly tests
   
   findbugs .................. Run Findbugs against Butterfly     
   pmd ....................... Run PMD against Butterfly     
   cpd ....................... Run Copy/Paste Detection against Butterfly     
   jslint .................... Run JSlint against Butterfly

   whitespace <extension> .... Normalize whitespace in files with the given extension
   
   clean ..................... Clean compiled classes
   distclean ................. Remove all generated files
                
EOF
    exit 1
}
                
add_option() {
    OPTS="$OPTS $1"
}
                
load_configs() {
    cat $1 | egrep "^[A-Z]" | sed 's/^\(.*\)$/export \1/' > .$1
    . ./.$1
    rm ./.$1
}
    
absolutize() {
  case $1 in
    /*) echo $1; ;;
    *) echo `pwd`/$1; ;;
  esac
}
    
check_macosx() {
    if ! $DARWIN ; then
        error "This action can only run on MacOSX"
    fi
}

check_downloaders() {
    CURL="`which curl 2> /dev/null`"
    WGET="`which wget 2> /dev/null`"
    
    if [ -z "$CURL"  -a   -z "$WGET" ]; then
        error "We need either 'curl' or 'wget' present in PATH to download external dependencies."
    fi
}

check_unzip() {
    UNZIP="`which unzip 2> /dev/null`"
    
    if [ -z "$UNZIP" ]; then
        error "We need 'unzip' present in PATH to expand external dependencies."
    fi
}

check_running() {
    check_downloaders
    URL="http://${SERVER_HOST}:${SERVER_PORT}/"
    
    if [ "$CURL" ]; then
        NOT_RUNNING=`curl -s $URL > /dev/null || echo not_running`
    elif [ "$WGET" ]; then
        NOT_RUNNING=`wget -q -O - $URL > /dev/null || echo not_running`
    fi    
}

get_revision() {
    if [ -d ".svn" ]; then
        INFO=`svn info`
    elif [ -d ".git" ]; then
        INFO=`git svn info`
    else
        error "cannot obtain revision, exiting!"
    fi

    REVISION=`echo $INFO | sed 's/.*Revision: /r/' | sed 's/ .*//'`
}
    
download() {
    URL=$1
    DEST=$2
    
    check_downloaders
    
    if [ "$CURL" ]; then
        curl -L -o $DEST $URL || exit "Error while downloading $URL"
    elif [ "$WGET" ]; then
        wget -O $DEST $URL || error "Error while downloading $URL"
    fi
}

tool_download() {
    URL=$1
    FILE=$2
    DIR=$3
    
    cd $TOOLS_DIR
        if [ ! -f "$FILE" ]; then
            download $URL $FILE
        fi
        if [ ! -d "$DIR" ]; then
            if [ -z "`echo $FILE | sed 's@.*.tar.gz$@@' | sed 's@.*.tgz$@@'`" ]; then
                tar xzf $FILE || error "Error while expanding $FILE"
            fi
            if [ -z "`echo $FILE | sed 's@.*.zip$@@'`" ]; then
                check_unzip
                $UNZIP -q $FILE || error "Error while expanding $FILE"
            fi
        fi
    cd ..
}

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

build_prepare() {
    if [ ! -d $BUILD_DIR ]; then 
        mkdir $BUILD_DIR || error "Error while making directory $BUILD_DIR"
    fi
}

dist_prepare() {
    if [ ! -d $DIST_DIR ]; then 
        mkdir $DIST_DIR || error "Error while making directory $DIST_DIR"
    fi
}

tools_prepare() {
    if [ ! -d $TOOLS_DIR ]; then 
        mkdir $TOOLS_DIR || error "Error while making directory $TOOLS_DIR"
    fi
}
        
appengine_prepare() {
    if [ -z "$APPENGINE_HOME" ]; then
        error "You have to have the APPENGINE_HOME environment variable set and pointing to the local installation of the Google AppEngine SDK."
    elif [ ! -f "$APPENGINE_HOME/bin/appcfg.sh" ]; then
        error "Environment variable APPENGINE_HOME is set to '$APPENGINE_HOME' which doesn't point to a valid Google App Engine SDK."
    fi
    APPENGINE="$APPENGINE_HOME/bin/appcfg.sh"
    APPENGINE_LOCAL="$APPENGINE_HOME/bin/dev_appserver.sh"
    
    ANT_PARAMS="$ANT_PARAMS -Dappengine.sdk.dir=$APPENGINE_HOME"
}

ant_prepare() {
    tools_prepare

    ANT_URL="http://archive.apache.org/dist/ant/binaries/apache-ant-1.8.1-bin.tar.gz"
    ANT_FILE=`echo $ANT_URL | sed 's|.*/||'`
    ANT_DIR="apache-ant-1.8.1"
    
    ANT="`which ant 2> /dev/null`"
    if [ -z "$ANT" ]; then
        if [ -z "$ANT_HOME" ]; then
            cd $TOOLS_DIR
                if [ ! -f "$ANT_FILE" ]; then
                    download $ANT_URL $ANT_FILE
                fi
                if [ ! -d "$ANT_DIR" ]; then
                    tar xzf $ANT_FILE -C . || error "Error while expanding $ANT_FILE"
                fi
                export ANT_HOME="`pwd`/$ANT_DIR"
                if $CYGWIN ; then
                    export ANT_HOME=`cygpath --unix "$ANT_HOME"`
                fi
            cd ..
        fi
        ANT="$ANT_HOME/bin/ant"
    fi
    
    get_revision
}
       
findbugs_prepare() {
    tools_prepare
    
    FINDBUGS_URL="http://downloads.sourceforge.net/project/findbugs/findbugs/1.3.9/findbugs-1.3.9.tar.gz"
    FINDBUGS_FILE=`echo $FINDBUGS_URL | sed 's|.*/||'`
    FINDBUGS_DIR="findbugs-1.3.9"

    tool_download $FINDBUGS_URL $FINDBUGS_FILE $FINDBUGS_DIR
}

pmd_prepare() {
    tools_prepare
    
    PMD_URL="http://downloads.sourceforge.net/project/pmd/pmd/4.2.5/pmd-bin-4.2.5.zip"
    PMD_FILE="pmd-bin-4.2.5.zip"
    PMD_DIR="pmd-4.2.5"

    tool_download $PMD_URL $PMD_FILE $PMD_DIR
}

jslint_prepare() {
    tools_prepare
    
    JSLINT_URL="http://jslint4java.googlecode.com/files/jslint4java-1.3.3-dist.zip"
    JSLINT_FILE="jslint4java-1.3.3-dist.zip"
    JSLINT_DIR="jslint4java-1.3.3"

    tool_download $JSLINT_URL $JSLINT_FILE $JSLINT_DIR
}
      
# ----------------------------------------------------------------------------------------------

ant() {
    ant_prepare   
    
    #export ANT_OPTS="-Xmx1024M"
    
    "$ANT" -f build.xml $ANT_PARAMS -Drevision="$REVISION" $1 || error "Error while running ant task '$1'"
}

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

test() {
    ant build_tests
    echo ""
    
    CLASSPATH="$TEST_DIR/classes${SEP}$WEBAPP/WEB-INF/classes${SEP}$SERVER_CLASSES_DIR${SEP}$TEST_DIR/lib/*${SEP}$SERVER_LIB_DIR/*${SEP}$WEBAPP/WEB-INF/lib/*"

    if [ -z "$1" ]; then
        TESTS="-excludegroups broken $TEST_DIR/conf/tests.xml"
    else 
        TESTS="-testclass $1"
    fi
    
    RUN_CMD="$JAVA -cp $CLASSPATH $OPTS org.testng.TestNG -d $BUILD_DIR/tests -listener org.testng.reporters.DotTestListener $TESTS"
    
    #echo "$RUN_CMD"
    #echo ""
    
    $RUN_CMD || error "Failed passing tests"
}

run() {
    FORK=$1
    
    if [ ! -d $SERVER_CLASSES_DIR ]; then
        IS_JAR=`ls $SERVER_LIB_DIR | grep butterfly`
        if [ -z "$IS_JAR" ]; then
            ant build
            echo ""
        fi
    fi
    
    check_running
    
    if [ -z "$NOT_RUNNING" ]; then
        warn "Butterfly is already running."
    fi
    
    if [ -d $SERVER_CLASSES_DIR ]; then
        add_option "-Dserver.autoreload=true"
        add_option "-Dbutterfly.autoreload=true"
    fi
                    
    CLASSPATH="$SERVER_CLASSES_DIR${SEP}$SERVER_LIB_DIR/*"

    RUN_CMD="$JAVA -cp $CLASSPATH $OPTS edu.mit.simile.butterfly.ButterflyServer"

    #echo "$RUN_CMD"
    #echo ""
  
    echo "Starting Butterfly at 'http://${SERVER_HOST}:${SERVER_PORT}/'"
    echo ""

    if [ -z "$FORK" ]; then
        exec $RUN_CMD
    else
        $RUN_CMD &
        SERVER_PID="$!"
    fi
}

appengine_build() {
    appengine_prepare
    
    ANT_PARAMS="-Dappengine.sdk.dir=${APPENGINE_HOME}"
    if [ "$1" ]; then
        ANT_PARAMS="$ANT_PARAMS -Dappengine.version=$1"
    fi
        
    ant prepare_webapp_appengine
}
       
appengine_upload() {
    appengine_build $1
    "$APPENGINE" update "$BUILD_DIR/appengine"
}

appengine_run() {
    appengine_build $1
    "$APPENGINE_LOCAL" "$BUILD_DIR/appengine"
}

findbugs() {
    findbugs_prepare
    
    ANT_PARAMS="-Dfindbugs.dir=${TOOLS_DIR}/${FINDBUGS_DIR}"
    ant findbugs
    
    open "$BUILD_DIR/reports/findbugs.html"
}    

pmd() {
    pmd_prepare
    
    ANT_PARAMS="-Dpmd.dir=${TOOLS_DIR}/${PMD_DIR}"
    ant pmd
    
    open "$BUILD_DIR/reports/pmd.html"
}    

cpd() {
    pmd_prepare
    
    ANT_PARAMS="-Dpmd.dir=${TOOLS_DIR}/${PMD_DIR}"
    ant cpd

    open "$BUILD_DIR/reports/cpd.txt"
}    

jslint() {
    jslint_prepare

    ANT_PARAMS="-Djslint.dir=${TOOLS_DIR}/${JSLINT_DIR}"
    ant jslint

    open "$BUILD_DIR/reports/jslint.txt"
}

whitespace() {
    [ $# -gt 0 ] || usage
    
    for i in `find . -name *.$1`; do
        # expand tabs to spaces
        expand -t 4 < $i > $i.1
        
        # convert DOS to UNIX newlines
        tr -d '\r' < $i.1 > $i.2
            
        rm $i $i.1
        mv $i.2 $i
    done
}   
    
# -------------------------- script -----------------------------
    
# ----- Normalize the current directory -------------------------

cd `dirname $0`

# ----- Default values ------------------------------------------

OPTS=""

# ---- OS-specific support --------------------------------------

SYSTEM=`uname`

CYGWIN=false
DARWIN=false
case "$SYSTEM" in
  CYGWIN*) CYGWIN=true ;;
  Darwin*) DARWIN=true ;;
esac

SEP=":" 
if $CYGWIN ; then
    SEP=";" 
fi

# ----- Load configurations -------------------------------------

load_configs butterfly.ini

# ----- Make sure there is an appropriate java environment is available -------------

if $DARWIN ; then
    if [ -z "$JAVA_HOME" ]; then
        # Mac OS X defaults to Java 5. So update JAVA_HOME unless the user manually set it.
        export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home"
    fi
fi

JAVA="`which java 2> /dev/null`"
if [ -z "$JAVA" ]; then
    if [ "$JAVA_HOME" ]; then
        JAVA="$JAVA_HOME/bin/java"
        if [ ! -f "$JAVA" ] ; then
            error "Could not find the 'java' executable at '$JAVA', are you sure your JAVA_HOME environment variable is pointing to a proper java installation?"
        fi
    else
        error "The 'java' command should be in your path or the 'JAVA_HOME' environment variable should be set"
    fi
fi

JAVA_VERSION=`$JAVA -version 2>&1 | grep version | cut -d ' ' -f 3 | egrep ^\"1\.6`
if [ -z "$JAVA_VERSION" ]; then
    error "Butterfly requires Java version 6 or later. If you have multiple versions of Java installed, please set JAVA_HOME to the correct version."
fi

# ----- Parse the command line args ------------------------------------------

while [ $# -ne 0 ] ; do
  case "$1" in
    -h) usage;;
    -p) shift; SERVER_PORT="$1"; shift; continue;;
    -i) shift; SERVER_HOST="$1"; shift; continue;;
    -w) shift; WEBAPP="$1"; shift; continue;;
    -m) shift; SERVER_MEMORY="$1"; shift; continue;;
    --debug) shift; add_option '-Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n'; continue;;
    --jmx) shift; add_option '-Dcom.sun.management.jmxremote'; continue;;
    -*) fail "Invalid option: $1";;
    *) break;;
  esac
done

if [ $# -ne 0 ]; then
    ACTION=$1; shift
fi

if [ -z "$ACTION" ]; then
    ACTION="run"
fi

# ----- Verify and Set Required Environment Variables -------------------------

if [ -z "$JAVA_OPTIONS" ]; then
  JAVA_OPTIONS=""
fi
add_option "$JAVA_OPTIONS"

if [ -z "$SERVER_MEMORY" ]; then
    SERVER_MEMORY="1024M"
fi
add_option "-Xms256M -Xmx$SERVER_MEMORY -Dserver.memory=$SERVER_MEMORY"

if [ -z "$SERVER_PORT" ]; then
    SERVER_PORT="8080"
fi
add_option "-Dserver.port=$SERVER_PORT"

if [ -z "$SERVER_HOST" ]; then
    SERVER_HOST="127.0.0.1"
fi
add_option "-Dserver.host=$SERVER_HOST"

if [ -z "$SERVER_CLASSES_DIR" ]; then
    SERVER_CLASSES_DIR="server/classes"
fi

if [ -z "$SERVER_LIB_DIR" ]; then
    SERVER_LIB_DIR="server/lib"
fi

if [ -z "$WEBAPP" ]; then
    WEBAPP="main/webapp"
fi
add_option "-Dserver.webapp=$WEBAPP"

if [ -z "$TEST_DIR" ]; then
    TEST_DIR="main/tests"
fi

if [ -z "$BUILD_DIR" ]; then
    BUILD_DIR="build"
fi

if [ -z "$TOOLS_DIR" ]; then
    TOOLS_DIR="tools"
fi

if [ -z "$DIST_DIR" ]; then
    DIST_DIR="dist"
fi

# ----------- Butterfly Properties -----------------------------------

if [ -z "$BUTTERFLY_WIRINGS" ]; then
    BUTTERFLY_WIRINGS="`absolutize modules.properties`"
fi
add_option "-Dbutterfly.modules.wirings=$BUTTERFLY_WIRINGS";

if [ -z "$BUTTERFLY_MODULES_PATH" ]; then
    BUTTERFLY_MODULES_PATH="`absolutize modules`"
fi
add_option "-Dbutterfly.modules.path=$BUTTERFLY_MODULES_PATH";

if [ "$BUTTERFLY_PROPERTIES" ]; then
    add_option "-Dbutterfly.properties=`absolutize $BUTTERFLY_PROPERTIES`";
fi

if [ "$BUTTERFLY_DEFAULT_ZONE" ]; then
    add_option "-Dbutterfly.default.zone=$BUTTERFLY_DEFAULT_ZONE";
fi

# ----- Respond to the action given --------------------------------------------

case "$ACTION" in
  build) build_prepare; ant jar;;
  clean) ant clean;;
  whitespace) whitespace $1;;
  distclean) ant distclean;;
  test) test $1;;  
  tests) test $1;;  
  findbugs) findbugs;;  
  pmd) pmd;;  
  cpd) cpd;;  
  jslint) jslint;;  
  appengine_build) appengine_build $1;;
  appengine_run) appengine_run $1;;
  appengine_upload) appengine_upload $1;;
  run) run;;  
  *) usage; ;;
esac
