#!/bin/bash

# Copyright (C) 2005 - 2020  Eric Van Dewoestine
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

if [ "$(whoami)" == "root" ] ; then
  echo "eclimd should not be run as root."
  echo "You need to run eclimd as the same user as your editor (vim, emacs, etc)."
  exit 1
fi

usage(){
  echo "Usage: eclimd [eclimopt ...] [jvmarg ...]"
  echo "  Note: the following eclim options, if supplied, must precede any jvmargs, etc."
  echo "  -b/--background  start eclimd in the background."
  echo "  -d/--debug       enable eclimd's debug logging."
  echo "  -f/--file <file> specify the location of an eclimrc file other than the"
  echo "                   default (~/.eclimrc). Must be the first option supplied."
  echo ""
  echo "Note: eclimd also supports running the eclipse garbage collector to"
  echo "      to remove stale bundles after upgrades, etc.:"
  echo "  $ eclimd gc"
}

if [ "$1" = "-?" -o "$1" = "-h" -o "$1" = "--help" ] ; then
  usage
  exit 0
fi

resolve_java(){
  if [ -z "$JAVA" ] ; then
    if [ -n "$JAVA_HOME" ] ; then
      if [ -x "$JAVA_HOME/bin/java" ] ; then
        JAVA="$JAVA_HOME/bin/java"
      elif [ -x "$JAVA_HOME/jre/bin/java" ] ; then
        JAVA="$JAVA_HOME/jre/bin/java"
      fi
    else
      JAVA=$(command -v java)
    fi
  fi

  if [ -z "$JAVA" ] ; then
    echo "Unable to locate your java install."
    echo "Please set your JAVA_HOME environment variable or"
    echo "set JAVA to the location of your java executable."
    exit 1
  fi

  # Note: as of java 9, the version string format has changed from something like
  # "1.8.0_144" in java 8 to just "9", or "9.1.1" in java 9
  # first pass just gets the full version, for all java versions
  JAVA_VERSION=$(
    $JAVA -version 2>&1 |
      grep version |
      perl -pe 's|.*"([0-9].*)".*|\1|'
  )
  # finish parsing the version for java 8 and older (version starts with "1.")
  if [[ "$JAVA_VERSION" == 1.* ]] ; then
    JAVA_VERSION=$(echo $JAVA_VERSION | perl -pe 's|1\.(.*)\..*|\1|')
  # finish parsing the version for java 9 and newer
  else
    JAVA_VERSION=$(echo $JAVA_VERSION | perl -pe 's|([0-9]+)\..*|\1|')
  fi

  # cast to an int
  JAVA_VERSION=$(($JAVA_VERSION + 0))
}

##
# Set ECLIM_HOME to eclim's home directory.
#
resolve_eclim_home(){
  FILEPATH="$0"

  # handle symlink to eclimd script
  # readlink -f is easier, but not supported on mac or bsd
  while [ -h "$FILEPATH" ] ; do
    PREV=$FILEPATH
    FILEPATH="$(readlink "$FILEPATH")"
    if [ -z "$FILEPATH" -o "$FILEPATH" = "$PREV" ] ; then
      FILEPATH=$PREV
      break
    fi

    # handle relative symlinks (neither eclim build nor installer create these,
    # so must have been created by the user or 3rd party installer)
    if [ ! -f "$FILEPATH" ] ; then
      PREVDIR="$(dirname "$PREV")"
      FILEPATH="$PREVDIR/$FILEPATH"
    fi
  done

  CURPATH="$(dirname "$FILEPATH")"
  ECLIM_HOME="$(cd "$CURPATH/.." ; pwd)"
}

##
# Set ECLIM_ECLIPSE_HOME to the eclipse home path.
#
resolve_eclipse_home(){
  #${eclipse.home}
  if [ -z "$ECLIM_ECLIPSE_HOME" -o ! -d "$ECLIM_ECLIPSE_HOME" ]; then
    ECLIM_ECLIPSE_HOME="$(cd "$ECLIM_HOME/../../"; pwd)"
  fi
}

##
# Set ECLIPSE_LAUNCHER to the eclipse launcher jar path.
#
resolve_eclipse_launcher(){
  PLUGINS="$ECLIM_ECLIPSE_HOME/plugins"

  # find the launcher jar (last modified if there is more than one)
  ECLIPSE_LAUNCHER="$(
    find "$PLUGINS" \
      -name 'org.eclipse.equinox.launcher_*.jar' \
      -exec ls -1t "{}" + | head -n1
  )"

  if [ ! -e "$ECLIPSE_LAUNCHER" ]; then
    echo "Unable to locate the eclipse launcher jar." 1>&2
    exit 1
  fi
}

validate_java_version(){
  eclipse_ini=$ECLIM_ECLIPSE_HOME/eclipse.ini
  if [ ! -f $eclipse_ini ] ; then
    echo "Unable to locate eclipse.ini at $ECLIM_ECLIPSE_HOME"
    exit 1
  fi

  required=$(cat $eclipse_ini | grep requiredJavaVersion | head -1 | perl -pe 's|.*=||')
  # java 8 or older
  if [[ "$required" == 1.* ]] ; then
    required=$(echo $required | perl -pe 's|1\.(.*)\..*|\1|')
  # java 9 or newer
  else
    required=$(echo $required | perl -pe 's|([0-9]+)\..*|\1|')
  fi
  # cast to an int
  required=$(($required + 0))

  # Note: see install.bin as well
  if [[ $JAVA_VERSION -lt $required ]] ; then
    echo "JAVA_VERSION=$JAVA_VERSION"
    echo "You must have at least java $required installed to run eclimd."
    echo "Note: you can still target older java versions on a per project basis."
    exit 1
  fi
}

##
# Builds a list of vm args to be passed to eclipse and sets ECLIM_VMARGS
# accordingly.
#
build_vmargs(){
  #ECLIMD_OPTS="-Djava.ext.dirs"
  if [ -n "$ECLIMD_OPTS" ] ; then
    ECLIM_VMARGS=("${ECLIM_VMARGS[@]}" "$ECLIMD_OPTS")
  fi

  # attempt to grab properties from .eclimrc if available.
  PERL="$(command -v perl)"
  if [ -f "$ECLIMRC" -a -n "$PERL" ] ; then
    IFS=$'\n'
    for vmarg in $(cat "$ECLIMRC" | perl -pe '
        # remove leading/trailing whitespace
        s|^\s+||g ; s|\s+$|\n|g ;
        # delete comment lines
        s|^#.*$||g ;
        # delete empty lines
        s|^\n||g ;
        # remove line continuations
        s|\\\\\n||g ;
        # add -D for each property
        s|^\s*([a-zA-Z])|-D\1|g ;
      ')
    do
      arg=$(echo "$vmarg" | perl -pe 's|(-D.*?)=.*|\1|')
      arg=$(echo "$arg" | perl -pe 's|\d+.*||')
      if [[ "${ECLIM_VMARGS[@]}" != *"$arg"* ]] ; then
        ECLIM_VMARGS=("${ECLIM_VMARGS[@]}" $vmarg)
      fi
    done
  fi

  ECLIPSE_INI="$ECLIM_ECLIPSE_HOME/eclipse.ini"

  # ensure that the correct jvm environment is used, but only force the
  # architecture if we find the swt jars, since linux distros may have the swt
  # libraries outside of eclipse.
  SWT="$(ls $ECLIM_ECLIPSE_HOME/plugins/org.eclipse.swt.*.jar 2> /dev/null)"
  if [ -z "$SWT" ] ; then
    P2POOL=$(
      grep -A 1 -- \
        "--launcher\.library" \
        $ECLIM_ECLIPSE_HOME/eclipse.ini 2> /dev/null | \
        tail -1
    )
    if [[ "$P2POOL" == ..* ]] ; then
      P2POOL="$(cd "$ECLIM_ECLIPSE_HOME/$P2POOL" ; pwd)"
      P2POOL="$(dirname "$(dirname "$P2POOL")")"
    fi
    if [ -n "$P2POOL" ] ; then
      SWT="$(ls $P2POOL/plugins/org.eclipse.swt.*.jar 2> /dev/null)"
    fi
  fi
  if [ -n "$SWT" ] ; then
    if $(echo "$SWT" | grep -q "x86_64\|aarch64") ; then
      ARCH_NAME="64-bit"
      ARCH=-d64
    else
      ARCH_NAME="32-bit"
      ARCH=-d32
    fi

    if $($JAVA $ARCH -version 2> /dev/null) ; then
      ECLIM_VMARGS=($ARCH "${ECLIM_VMARGS[@]}")
    # as of jdk 10, oracle doesn't support the -d<arch> option, so fall back
    # to checking version output
    elif ! $($JAVA -version 2>&1 | grep -i "$ARCH_NAME" > /dev/null) ; then
      echo "Your jvm does not support the architecture required " \
        "for the version of eclipse you have installed: $ARCH ($ARCH_NAME)"
      exit 1
    fi
  fi

  # add any eclipse vmargs not present
  if [ -f "$ECLIPSE_INI" -a -n "$PERL" ] ; then
    CONTENTS=$(cat "$ECLIPSE_INI" | perl -pe 's|\n| |g')
    IFS=$'\n'
    IS_VMARGS=0
    for line in $(cat "$ECLIPSE_INI") ; do
      if [[ $IS_VMARGS -ne 1 ]] ; then
        if [[ "$line" == "-vmargs"* ]] ; then
          IS_VMARGS=1
        fi
        continue
      fi

      vmarg=$line
      arg=$(echo "$vmarg" | perl -pe 's|(-D.*?)=.*|\1|')
      arg=$(echo "$arg" | perl -pe 's|\d+.*||')
      # ignore dock icon since it's probably on OSX and relative to
      # Eclipse.app/Contents
      if [[ "$vmarg" == *-Xdock:icon* ]] ; then
        continue
      fi
      # ignore oomph (eclipse installer) crap
      if [[ "$vmarg" == *oomph* ]] ; then
        continue
      fi
      # ignore java 9 options if the current env is running a lower java
      # version.
      if [[ $JAVA_VERSION -lt 9 ]] ; then
        if [[ "$vmarg" == *--add-modules* ]] ; then
          continue
        fi
        if [[ "$vmarg" == *--permit-illegal-access* ]] ; then
          continue
        fi
        # UseStringDeduplication was added in java 8 update 20, but the user
        # may have a java 8 version without that support.
        if [[ "$vmarg" == *UseStringDeduplication* ]] ; then
          continue
        fi
      fi
      if [[ "${ECLIM_VMARGS[@]}" != *"$arg"* ]] ; then
        ECLIM_VMARGS=("${ECLIM_VMARGS[@]}" $vmarg)
      fi
    done
  fi

  # set a default for max heap space
  if [[ "${ECLIM_VMARGS[@]}" != *-Xmx* ]]; then
    ECLIM_VMARGS=("${ECLIM_VMARGS[@]}" -Xmx256m)
  fi

  # for osx
  if [ $DARWIN = true ] ; then
    if [[ "${ECLIM_VMARGS}[@]" != *-XstartOnFirstThread* ]]; then
      ECLIM_VMARGS=("${ECLIM_VMARGS[@]}" -XstartOnFirstThread)
    fi
  fi
}

APPLICATION=org.eclim.application
ECLIMRC=$HOME/.eclimrc

DEBUG=0
BACKGROUND=0
while true ; do
  case "$1" in
    -d | --debug )
      DEBUG=1; shift ;;
    -b | --background )
      BACKGROUND=1; shift ;;
    -f | --file )
      if [ -f "$2" ] ; then
        ECLIMRC="$2"
      elif [ -z "$2" -o "${2:0:1}" = '-' ] ; then
        echo "please supply the path to the eclimrc file to load"
        exit 1
      else
        echo "file not found: $2"
        exit 1
      fi
      shift
      shift
      ;;
    gc )
      APPLICATION=org.eclipse.equinox.p2.garbagecollector.application
      shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

resolve_java
resolve_eclim_home
resolve_eclipse_home
resolve_eclipse_launcher
validate_java_version

DARWIN=false
if $(uname -a | grep -iq "darwin") ; then
  DARWIN=true
fi

declare -a ECLIM_VMARGS=("$@")
build_vmargs
if [ $DEBUG -eq 1 ] ; then
  ECLIM_VMARGS=("${ECLIM_VMARGS[@]}" -Dorg.eclim.debug)
fi

ARGS=(
  "${ECLIM_VMARGS[@]}"
  -jar
  "$ECLIPSE_LAUNCHER"
  --launcher.suppressErrors
  -debug
  -clean
  -refresh
  -application $APPLICATION
)

if [ $BACKGROUND -eq 1 ]; then
  CLASSPATH="" $JAVA "${ARGS[@]}" &> /dev/null &
else
  echo $JAVA -version
  $JAVA -version
  echo "JAVA_VERSION=$JAVA_VERSION"
  echo ""
  echo $JAVA "${ARGS[@]}"
  CLASSPATH="" $JAVA "${ARGS[@]}"
fi
