#!/usr/bin/env bash
# bin/compile <build-dir> <cache-dir>

# fail fast
set -x
set -e

function error() {
  echo " !     $*"
  exit 1
}

function mktmpdir() {
  dir=$(mktemp -t node-$1-XXXX)
  rm -rf $dir
  mkdir -p $dir
  echo $dir
}

function indent() {
  c='s/^/       /'
  case $(uname) in
    Darwin) sed -l "$c";;
    *)      sed -u "$c";;
  esac
}

run_npm() {
  command="$1"
  HOME="$BUILD_DIR" $VENDORED_NODE/bin/node $VENDORED_NPM/cli.js $command 2>&1 | indent

  if [ "${PIPESTATUS[*]}" != "0 0" ]; then
    echo " !     Failed to $command dependencies with npm"
    exit 1
  fi
}

# clean up leaking environment
unset GIT_DIR

function manifest_versions() {
  curl "http://${S3_BUCKET}.s3.amazonaws.com/manifest.${1}" -s -o - | tr -s '\n' ' '
}

function resolve_versions() {
  args=""
  for version in $1; do args="${args} -v \"${version}\""; done
  for version in $2; do args="${args} -r \"${version}\""; done
  eval $LP_DIR/vendor/node-semver/bin/semver ${args} | tail -r | head -n1
}

# config
DEFAULT_NODE_VERSION="0.4.7"
DEFAULT_NPM_VERSION="1.0.94"
SCONS_VERSION="1.2.0"
S3_BUCKET="heroku-buildpack-nodejs"

# parse and derive params
BUILD_DIR=$1
CACHE_DIR=$2
LP_DIR=`cd $(dirname $0); cd ..; pwd`
CACHE_STORE_DIR="$CACHE_DIR/node_modules/$NPM_VERSION"
CACHE_TARGET_DIR="$BUILD_DIR/node_modules"

# bootstrap node
NODE_PACKAGE="http://${S3_BUCKET}.s3.amazonaws.com/nodejs-${DEFAULT_NODE_VERSION}.tgz"
VENDORED_NODE="$(mktmpdir node)"
echo "-----> Bootstraping Node.js"
mkdir -p $VENDORED_NODE  && curl $NODE_PACKAGE -s -o -  | tar xzf - -C $VENDORED_NODE
echo $VENDORED_NODE
ls -la $VENDORED_NODE
OLDPATH="$PATH"
PATH="$VENDORED_NODE/bin:$PATH"
echo $PATH

# determine available versions
NODEJS_VERSIONS=$(manifest_versions "nodejs")
NPM_VERSIONS=$(manifest_versions "npm")

# determine requested versions
engines=$(cat $BUILD_DIR/package.json | $LP_DIR/vendor/json/json engines 2>&1)

if [ $? == 0 ] && [ "$engines" != "" ]; then
  REQUESTED_NODE_VERSION=$(cat $BUILD_DIR/package.json | $LP_DIR/vendor/json/json engines.node)
  REQUESTED_NPM_VERSION=$(cat $BUILD_DIR/package.json | $LP_DIR/vendor/json/json engines.npm 2>&1)
else
  REQUESTED_NODE_VERSION=""
  REQUESTED_NPM_VERSION=""
fi

# reset path to undo bootstrapped node
PATH="$OLDPATH"

# resolve node version
if [ "${REQUESTED_NODE_VERSION}" == "" ]; then
  NODE_VERSION=${DEFAULT_NODE_VERSION}
else
  RESOLVED_NODE=$(resolve_versions "${NODEJS_VERSIONS}" "${REQUESTED_NODE_VERSION}")

  if [ "${RESOLVED_NODE}" == "" ]; then
    error "Requested Node.js version: ${REQUESTED_NODE_VERSION} does not match available versions: ${NODEJS_VERSIONS}"
  else
    NODE_VERSION=${RESOLVED_NODE}
  fi
fi
echo "-----> Using Node.js version: ${NODE_VERSION}"

# resolve npm version
if [ "${REQUESTED_NPM_VERSION}" == "" ]; then
  NPM_VERSION=${DEFAULT_NPM_VERSION}
else
  RESOLVED_NPM=$(resolve_versions "${NPM_VERSIONS}" "${REQUESTED_NPM_VERSION}")

  if [ "${RESOLVED_NPM}" == "" ]; then
    error "Requested npm version: ${REQUESTED_NPM_VERSION} does not match available versions: ${NPMJS_VERSIONS}"
  else
    NPM_VERSION=${RESOLVED_NPM}
  fi
fi
echo "-----> Using npm version: ${NPM_VERSION}"

# s3 packages
NODE_PACKAGE="http://${S3_BUCKET}.s3.amazonaws.com/nodejs-${NODE_VERSION}.tgz"
NPM_PACKAGE="http://${S3_BUCKET}.s3.amazonaws.com/npm-${NPM_VERSION}.tgz"
SCONS_PACKAGE="http://${S3_BUCKET}.s3.amazonaws.com/scons-${SCONS_VERSION}.tgz"

# vendor directories
VENDORED_NODE="$(mktmpdir node)"
VENDORED_NPM="$(mktmpdir npm)"
VENDORED_SCONS="$(mktmpdir scons)"

# download and unpack packages
echo "-----> Fetching Node.js binaries"
mkdir -p $VENDORED_NODE  && curl $NODE_PACKAGE -s -o -  | tar xzf - -C $VENDORED_NODE
mkdir -p $VENDORED_NPM   && curl $NPM_PACKAGE -s -o -   | tar xzf - -C $VENDORED_NPM
mkdir -p $VENDORED_SCONS && curl $SCONS_PACKAGE -s -o - | tar xzf - -C $VENDORED_SCONS

# vendor node into the slug
PATH="$BUILD_DIR/bin:$PATH"
echo "-----> Vendoring node into slug"
mkdir -p "$BUILD_DIR/bin"
cp "$VENDORED_NODE/bin/node" "$BUILD_DIR/bin/node"

# setting up paths for building
PATH="$VENDORED_SCONS:$VENDORED_NODE/bin:$PATH"
INCLUDE_PATH="$VENDORED_NODE/include"
export CPATH="$INCLUDE_PATH"
export CPPPATH="$INCLUDE_PATH"

# unpack existing cache
if [ -d $CACHE_STORE_DIR ]; then

  # generate a place to put node_modules
  TEMP_NODE_MODULES_DIR=$(mktmpdir node_modules)

  # move existing node_modules out of the way
  if [ -d $CACHE_TARGET_DIR ]; then
    mv $CACHE_TARGET_DIR $TEMP_NODE_MODULES_DIR/
  fi

  # copy the cached node_modules in
  mkdir -p $CACHE_TARGET_DIR
  cp -R $CACHE_STORE_DIR/* $CACHE_TARGET_DIR/

  # move existing node_modules back into place
  if [ -d $TEMP_NODE_MODULES_DIR/node_modules ]; then
    cp -R $TEMP_NODE_MODULES_DIR/node_modules/* $CACHE_TARGET_DIR/
  fi

fi

# install dependencies with npm
echo "-----> Installing dependencies with npm"
cd $BUILD_DIR
HOME="$BUILD_DIR" $VENDORED_NODE/bin/node $VENDORED_NPM/cli.js prune 2>&1 | indent
HOME="$BUILD_DIR" $VENDORED_NODE/bin/node $VENDORED_NPM/cli.js install 2>&1 | indent
if [ "${PIPESTATUS[*]}" != "0 0" ]; then
  error "Failed to install dependencies with npm"
else
  echo "       Dependencies installed"
fi

echo "Dependencies installed" | indent

# repack cache with new assets
if [ -d $CACHE_TARGET_DIR ]; then
  rm -rf $CACHE_STORE_DIR
  mkdir -p $(dirname $CACHE_STORE_DIR)
  cp -a $CACHE_TARGET_DIR $CACHE_STORE_DIR
fi
