#!/bin/sh
set -e

ELIXIR_VERSION=1.14.3

if [ $# -eq 0 ] || { [ $# -eq 1 ] && { [ "$1" = "--help" ] || [ "$1" = "-h" ]; }; }; then
  cat <<USAGE >&2
Usage: $(basename "$0") [options] [.exs file] [data]

## General options

  -e "COMMAND"                 Evaluates the given command (*)
  -h, --help                   Prints this message (standalone)
  -r "FILE"                    Requires the given files/patterns (*)
  -S SCRIPT                    Finds and executes the given script in \$PATH
  -pr "FILE"                   Requires the given files/patterns in parallel (*)
  -pa "PATH"                   Prepends the given path to Erlang code path (*)
  -pz "PATH"                   Appends the given path to Erlang code path (*)
  -v, --version                Prints Erlang/OTP and Elixir versions (standalone)

  --app APP                    Starts the given app and its dependencies (*)
  --erl "SWITCHES"             Switches to be passed down to Erlang (*)
  --eval "COMMAND"             Evaluates the given command, same as -e (*)
  --logger-otp-reports BOOL    Enables or disables OTP reporting
  --logger-sasl-reports BOOL   Enables or disables SASL reporting
  --no-halt                    Does not halt the Erlang VM after execution
  --short-version              Prints Elixir version (standalone)
  --werl                       Uses Erlang's Windows shell GUI (Windows only)

Options given after the .exs file or -- are passed down to the executed code.
Options can be passed to the Erlang runtime using \$ELIXIR_ERL_OPTIONS or --erl.

## Distribution options

The following options are related to node distribution.

  --cookie COOKIE              Sets a cookie for this distributed node
  --hidden                     Makes a hidden node
  --name NAME                  Makes and assigns a name to the distributed node
  --rpc-eval NODE "COMMAND"    Evaluates the given command on the given remote node (*)
  --sname NAME                 Makes and assigns a short name to the distributed node

## Release options

The following options are generally used under releases.

  --boot "FILE"                Uses the given FILE.boot to start the system
  --boot-var VAR "VALUE"       Makes \$VAR available as VALUE to FILE.boot (*)
  --erl-config "FILE"          Loads configuration in FILE.config written in Erlang (*)
  --pipe-to "PIPEDIR" "LOGDIR" Starts the Erlang VM as a named PIPEDIR and LOGDIR
  --vm-args "FILE"             Passes the contents in file as arguments to the VM

--pipe-to starts Elixir detached from console (Unix-like only).
It will attempt to create PIPEDIR and LOGDIR if they don't exist.
See run_erl to learn more. To reattach, run: to_erl PIPEDIR.

** Options marked with (*) can be given more than once.
** Standalone options can't be combined with other options.
USAGE
  exit 1
fi

readlink_f () {
  cd "$(dirname "$1")" > /dev/null
  filename="$(basename "$1")"
  if [ -h "$filename" ]; then
    readlink_f "$(readlink "$filename")"
  else
    echo "$(pwd -P)/$filename"
  fi
}

if [ $# -eq 1 ] && [ "$1" = "--short-version" ]; then
  echo "$ELIXIR_VERSION"
  exit 0
fi

# Stores static Erlang arguments and --erl (which is passed as is)
ERL=""

# Stores erl arguments preserving spaces/quotes (mimics an array)
erl_set () {
  eval "E${E}=\$1"
  E=$((E + 1))
}

# Checks if a string starts with prefix. Usage: starts_with "$STRING" "$PREFIX"
starts_with () {
  case $1 in
    "$2"*) true;;
    *) false;;
  esac
}

ERL_EXEC="erl"
MODE="elixir"
I=1
E=0
LENGTH=$#
set -- "$@" -extra

while [ $I -le $LENGTH ]; do
  # S counts to be shifted, C counts to be copied
  S=0
  C=0
  case "$1" in
    +iex)
        C=1
        MODE="iex"
        ;;
    +elixirc)
        C=1
        MODE="elixirc"
        ;;
    -v|--no-halt|--no-pry)
        C=1
        ;;
    -e|-r|-pr|-pa|-pz|--app|--eval|--remsh|--dot-iex)
        C=2
        ;;
    --rpc-eval)
        C=3
        ;;
    --hidden)
        S=1
        ERL="$ERL -hidden"
        ;;
    --logger-otp-reports)
        S=2
        if [ "$2" = 'true' ] || [ "$2" = 'false' ]; then
          ERL="$ERL -logger handle_otp_reports $2"
        fi
        ;;
    --logger-sasl-reports)
        S=2
        if [ "$2" = 'true' ] || [ "$2" = 'false' ]; then
          ERL="$ERL -logger handle_sasl_reports $2"
        fi
        ;;
    --erl)
        S=2
        ERL="$ERL $2"
        ;;
    --cookie)
        S=2
        erl_set "-setcookie"
        erl_set "$2"
        ;;
    --sname|--name)
        S=2
        erl_set "$(echo "$1" | cut -c 2-)"
        erl_set "$2"
        ;;
    --erl-config)
        S=2
        erl_set "-config"
        erl_set "$2"
        ;;
    --vm-args)
        S=2
        erl_set "-args_file"
        erl_set "$2"
        ;;
    --boot)
        S=2
        erl_set "-boot"
        erl_set "$2"
        ;;
    --boot-var)
        S=3
        erl_set "-boot_var"
        erl_set "$2"
        erl_set "$3"
        ;;
    --pipe-to)
        S=3
        RUN_ERL_PIPE="$2"
        RUN_ERL_LOG="$3"
        if [ "$(starts_with "$RUN_ERL_PIPE" "-")" ]; then
          echo "--pipe-to : PIPEDIR cannot be a switch" >&2 && exit 1
        elif [ "$(starts_with "$RUN_ERL_LOG" "-")" ]; then
          echo "--pipe-to : LOGDIR cannot be a switch" >&2 && exit 1
        fi
        ;;
    --werl)
        S=1
        if [ "$OS" = "Windows_NT" ]; then ERL_EXEC="werl"; fi
        ;;
    *)
        while [ $I -le $LENGTH ]; do
          I=$((I + 1))
          set -- "$@" "$1"
          shift
        done
        break
        ;;
  esac

  while [ $I -le $LENGTH ] && [ $C -gt 0 ]; do
    C=$((C - 1))
    I=$((I + 1))
    set -- "$@" "$1"
    shift
  done

  I=$((I + S))
  shift $S
done

I=$((E - 1))
while [ $I -ge 0 ]; do
  eval "VAL=\$E$I"
  set -- "$VAL" "$@"
  I=$((I - 1))
done

SELF=$(readlink_f "$0")
SCRIPT_PATH=$(dirname "$SELF")

if [ "$OSTYPE" = "cygwin" ]; then SCRIPT_PATH=$(cygpath -m "$SCRIPT_PATH"); fi
if [ "$MODE" != "iex" ]; then ERL="-noshell -s elixir start_cli $ERL"; fi

if [ "$OS" != "Windows_NT" ] && [ -z "$NO_COLOR" ]; then
  if test -t 1 -a -t 2; then ERL="-elixir ansi_enabled true $ERL"; fi
fi

# One MAY change ERTS_BIN= but you MUST NOT change
# ERTS_BIN=$ERTS_BIN as it is handled by Elixir releases.
for obsd_erts in /usr/local/lib/erlang25/erts-*; do
  ERTS_BIN="$obsd_erts/bin/";
done
ERTS_BIN="$ERTS_BIN"

set -- "$ERTS_BIN$ERL_EXEC" -pa "$SCRIPT_PATH"/../lib/*/ebin $ELIXIR_ERL_OPTIONS $ERL "$@"

if [ -n "$RUN_ERL_PIPE" ]; then
  ESCAPED=""
  for PART in "$@"; do
    ESCAPED="$ESCAPED $(echo "$PART" | sed 's/[^a-zA-Z0-9_\-\/]/\\&/g')"
  done
  mkdir -p "$RUN_ERL_PIPE"
  mkdir -p "$RUN_ERL_LOG"
  ERL_EXEC="run_erl"
  set -- "$ERTS_BIN$ERL_EXEC" -daemon "$RUN_ERL_PIPE/" "$RUN_ERL_LOG/" "$ESCAPED"
fi

if [ -n "$ELIXIR_CLI_DRY_RUN" ]; then
  echo "$@"
else
  exec "$@"
fi
