#!/bin/bash
#
# This is a "compiler" for JTalk code. Run without arguments for help.

# Get JTalk root directory from the location of this script.
JTALK=`dirname ${0}`/..

function usage {
	cat <<ENDOFHELP
Usage: $0 [-N|D] [-K|C] [-o] [-O] [-m class] [-M file] [-i] [-I file] file1 [file2 ...] [Program]

   Will compile Jtalk files - either separately or into a runnable complete program.
   Files listed will be handled using these rules:

   *.js
     Files are concatenated in listed order. If not found we look in $JTALK/js

   *.st
     Files are compiled into .js files before concatenated. If not found we look
     in $JTALK/st.

     NOTE: Each file is currently considered to be a fileout of a single class
     category of the same name as the file!

   If no Program is specified each given .st file will be compiled into a .js file.
   Otherwise a <Program>.js file is linked together based on the options:

  -N or -D
     Compile for Node.js or D8 (V8 shell). Implies "-K -i -m Main" so boot.js and Kernel.js
     are added first and init.js is added last with a call to Main class>>main.

  -K
     Add libraries to get minimal JTalk Kernel running.

  -C
     Add libraries to get minimal JTalk Compiler running.

  -o
     Optimize each js file using the Google closure compiler. Using ~/compiler.jar    

  -O
     Optimize <Program>.js using the Google closure compiler. Using ~/compiler.jar

  -l libray1,library2
     Load listed libraries (no spaces) in Compiler before compiling.

  -L
     Loads all libraries in directory js in Compiler before compiling.

  -i
     Add library standard initializer $JTALK/js/init.js

  -I file
     Add library initializer <file>.

  -m class
     Add call to #main in class <class>. 

  -M file
     Add javascript file <file> at the end acting as main.


     Example invocations:

     Compile Kernel.st to Kernel.js:

        jtalkc Kernel.st

     Compile Hello.st to Hello.js and create complete program called
     Program.js for Node.js including boot.js, Kernel.js, init.js and
     adding a call to class method #main in class Hello:

        jtalkc -N -m Hello Hello.st Program

     Compile two .st files into corresponding .js files,
     and manually link with specific myboot.js, myKernel.js, myinit.js and main.js
     and create complete program called Program.js:

        jtalkc -M main.js -I myinit.js myboot.js myKernel.js Cat1.st Cat2.st Program

ENDOFHELP
	exit 1;
}

# Check we at least got one argument
if [ -z $1 ] ; then
   usage
fi

# Define our predefined library combinations
BOOT="$JTALK/js/boot.js"
KERNEL="$BOOT $JTALK/js/Kernel.js"
COMPILER="$KERNEL $JTALK/js/Parser.js $JTALK/js/Compiler.js"
CANVAS="$COMPILER $JTALK/js/Canvas.js"

# Predefined initializer
INITIALIZER="$JTALK/js/init.js"

# Default values
ENV=
INIT=
MAIN=
MAINFILE=
BASE=$KERNEL
LOAD=

# Read options and shift them away
while getopts "NDKCoOl:LiI:M:m:h?" o; do
case "$o" in
   N) ENV=NODE
      BASE=$KERNEL
      INIT=$INITIALIZER
      MAIN=Main;;
   D) ENV=D8
      BASE=$KERNEL
      INIT=$INITIALIZER
      MAIN=Main;;
   K) BASE=$KERNEL;;
   C) BASE=$COMPILER;;
   o) CLOSURE=true
      CLOSUREPARTS=true;;
   O) CLOSURE=true
      CLOSUREFULL=true;;
   l) LOAD=$OPTARG;;
   L) LOAD="$JTALK/*.js";;
   I) INIT=$INITIALIZER;;
   i) INIT=$OPTARG;;
   M) MAINFILE=$OPTARG;;
   m) MAIN=$OPTARG;;
   h) usage;;
   [?])  usage;;
   esac
done
shift $(($OPTIND - 1))

# Check for Closure compiler and Java
if [ ! -z $CLOSURE ]; then
  java > /dev/null
  if [ $? -eq 0 ]; then 
    if [ ! -f ~/compiler.jar ]; then
      echo "Can not find Closure compiler at ~/compiler.jar"
      exit 1
    fi
  else
   echo "java is not installed and is needed for -O or -o (Closure compiler)."
   exit 1
  fi
fi

# Define our Compiler
# We need to add extra listed libraries in $LOAD not already in $CANVAS
OURCOMPILER="$CANVAS $JTALK/js/init.js $JTALK/nodejs/nodecompile.js"

# Combine supplied libraries
LIBS="$BASE $ADDONS"

# Get a unique tempdir and make it get auto removed on exit
TMPDIR=`mktemp -d`
trap "rm -rf $TMPDIR" EXIT

# --------------------------------------------------
# Collect libraries and Smalltalk files looking
# both locally and in $JTALK/js and $JTALK/st 
# --------------------------------------------------
PROGRAM=
until [ "$*" = "" ]
do
  case $1 in
     *.st)
        CATEGORY=`basename $1 .st`
        if [ -f "$1" ]; then
           COMPILE="$COMPILE $1 $CATEGORY"
           COMPILED="$COMPILED $CATEGORY.js"
        else
           if [ -f $JTALK/st/$1 ]; then
             COMPILE="$COMPILE $JTALK/st/$1 $CATEGORY"
             COMPILED="$COMPILED $CATEGORY.js"
           else
             echo "JTalk file not found: $1"
             exit 1
           fi
        fi
        ;;

     *.js)
        if [ -f "$1" ]; then
           LIBS="$LIBS $1" 
        else
           if [ -f $JTALK/js/$1 ]; then
             LIBS="$LIBS $JTALK/js/$1"
           else
             echo "Javascript file not found: $1"
             exit 1
           fi
        fi
        ;;
      *)
        # Will end up being the last non js/st argument
        PROGRAM=$1
        ;;
  esac
  shift
done

# --------------------------------------------------
# Actual compilation phase of collected .st files
# --------------------------------------------------

# Create compiler dynamically
cat $OURCOMPILER > $TMPDIR/compiler.js

# Compile all collected .st files to .js
echo "Compiling ..."
node $TMPDIR/compiler.js $COMPILE

# Verify all .js files corresponding to .st files were created, otherwise exit
IFS=" "
for FILE in $COMPILED
do
  if [ ! -f "$FILE" ]; then
    echo "Failed compilation of $FILE, exiting."
    exit 1
  fi 
done

if [ ! -z $CLOSUREPARTS ]; then
  echo "Compiling all js files using Google closure compiler."

  ALLJSFILES="$COMPILED $LIBS"
  for FILE in $ALLJSFILES
  do
    mv $FILE $FILE.original
    java -jar ~/compiler.jar --js $FILE.original --js_output_file $FILE
    rm $FILE.original
  done
fi


if [ -z $PROGRAM ]; then
  echo "Done."
  exit 0
fi

# --------------------------------------------------
# Now we start composing resulting javascript file.
# --------------------------------------------------

# Add collected libraries to libs.js file.
if [ ! -z "$LIBS" ]; then
  echo "Adding libraries $LIBS ..."
  cat $LIBS > $TMPDIR/libs.js
  LIBS=$TMPDIR/libs.js
fi

echo "Adding Jtalk code$COMPILED ..."

# Check for init file
if [ ! -z "$INIT" ]; then
   if [ -f "$INIT" ]; then
      echo "Adding initializer $INIT ..."
   else
      echo "Can not find init file $INIT, exiting."
      exit 1
   fi 
fi

# Check for adding main
if [ ! -z "$MAIN" ]; then
  echo "Adding call to $MAIN class >> main ..."
  echo "smalltalk.$MAIN._main()" > $TMPDIR/main.js
  MAIN=$TMPDIR/main.js
fi

# Check for adding main file
if [ ! -z "$MAINFILE" ]; then
   if [ -f "$MAINFILE" ]; then
      echo "Adding main as $MAINFILE ..."
   else
      echo "Can not find main file $MAINFILE, exiting."
      exit 1
   fi 
   MAIN=$MAINFILE
fi

# And finally concatenate Program.js
echo "Writing $PROGRAM.js ..."
cat $LIBS $COMPILED $INIT $MAIN > $PROGRAM.js
echo "Done."


if [ ! -z $CLOSUREFULL ]; then
  echo "Compiling $PROGRAM.js file using Google closure compiler."
  mv $PROGRAM.js $PROGRAM.js.original
  java -jar ~/compiler.jar --js $PROGRAM.js.original --js_output_file $PROGRAM.js
  rm $PROGRAM.js.original
  echo "Done."
fi