#! /usr/bin/env bash

# Licensed to the Apache Software Foundation (ASF) under one or more contributor license
# agreements. See the NOTICE file distributed with this work for additional information regarding
# copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the License. You may obtain a
# copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing permissions and limitations under
# the License.

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do
   bin="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
   SOURCE="$(readlink "$SOURCE")"
   [[ $SOURCE != /* ]] && SOURCE="$bin/$SOURCE"
done
# Set up variables needed by fluo-env.sh
export bin="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
export basedir="$( cd -P ${bin}/.. && pwd )"
export conf="$basedir/conf"
export lib="$basedir/lib"
export cmd="$1"
case "$cmd" in
  oracle|worker|init) export app="$2" ;;
esac
export FLUO_VERSION=1.2.0

if [ ! -f "$conf/fluo-env.sh" ]; then
  echo "fluo-env.sh must exist in $conf"
  exit 1
fi
source "$conf/fluo-env.sh"
export CLASSPATH
JAVA="java ${JAVA_OPTS[*]}"
if [ -f "$FLUO_CONN_PROPS" ]; then
  JAVA="$JAVA -Dfluo.conn.props=$FLUO_CONN_PROPS"
fi

deprecated_fluo_props=$conf/fluo.properties

if [[ -f "$FLUO_CONN_PROPS" && -f "$deprecated_fluo_props" ]]; then
  echo "Fluo is being configured by $FLUO_CONN_PROPS and $deprecated_fluo_props. Remove one of these files."
  exit 1
fi

# stop if any command fails
set -e

function copy_config {
  if [ -f "$conf/$1" ]; then
    cp "$conf/$1" "$APP_CONF_DIR/"
  elif [ -f "$conf/examples/$1" ]; then
    cp "$conf/examples/$1" "$APP_CONF_DIR"
  else
    echo "Config file $1 not found in $conf or $conf/examples"
    exit 1
  fi
}

function print_usage {
  echo -e "Usage: fluo <command> (<argument> ...)\n"
  echo -e "Possible commands:\n"
  echo "  init -a <app> -p <appProps>   Initializes Fluo application for <app> using <appProps>. Run with -h to see additional args."
  echo "  remove -a <app>               Removes Fluo application for <app>"
  echo "  classpath                     Prints the classpath setup in fluo-env.sh"
  echo "  config -a <app>               Prints application configuration stored in Zookeeper for <app>"
  echo "  get-jars -a <app> -d <dir>    Copies <app> jars from DFS to local <dir>"
  echo "  list                          Lists all Fluo applications in Fluo instance"
  echo "  scan -a <app>                 Prints snapshot of data in Fluo <app>"
  echo "  status -a <app>               Prints status of Fluo application for <app>"
  echo "  oracle -a <app>               Starts Fluo Oracle process for <app>"
  echo "  worker -a <app>               Starts Fluo Worker process for <app>"
  echo "  version                       Prints the version of Fluo"
  echo "  wait -a <app>                 Waits until all notifications are processed for <app>"
  echo "  exec <app> <class> {<arg>}    Executes <class> with <args> using classpath for <app>";

  echo -e "\nDeprecated commands (available if fluo.properties exists):\n"
  echo "  new <app>     (Deprecated) Creates configuration for new application in apps/"
  echo "  start <app>   (Deprecated) Starts Fluo application on cluster"
  echo "  stop <app>    (Deprecated) Stops Fluo application on cluster"
  echo "  init <app>    (Deprecated) Initializes Fluo application using configuration in apps/<app>/conf/fluo.properties"
  echo "  kill <app>    (Deprecated) Kills Fluo application on cluster"
  echo "  info <app>    (Deprecated) Prints information about containers of Fluo application"
  echo " "
  exit 1
}

function check_conn_props {
  if [ ! -f "$FLUO_CONN_PROPS" ]; then
    echo "$FLUO_CONN_PROPS must exist!"
    exit 1
  fi
}

function verify_app {
  if [ -z "$1" ]; then
    echo -e "The application name (set by <app>) cannot be an empty string!\n"
    print_usage
  fi
  if [[ $1 = *"-h"* ]]; then
    print_usage
  fi
}

function deprecated_verify {
  verify_app "$1"
  APP=$1
  APP_DIR=$basedir/apps/$APP
  APP_CONF_DIR=$APP_DIR/conf
  APP_LIB_DIR=$APP_DIR/lib
  if [ ! -f "$deprecated_fluo_props" ]; then
    echo "ERROR - This command is deprecated can only be used if fluo.properties exists in $conf"
    exit 1
  fi
}

function deprecated_verify_full {
  deprecated_verify "$1"
  java org.apache.fluo.cluster.util.ValidateAppName "$APP"
  if [[ ! -d $APP_DIR || ! -d $APP_CONF_DIR || ! -d $APP_LIB_DIR  ]]; then
    echo "ERROR - The Fluo '$APP' application needs to be configured in apps/ with a conf/ and lib/ directory.  Use 'fluo new $APP' to create this configuration"
    exit 1
  fi
}

function check_hadoop {
  if [[ -z $HADOOP_PREFIX ]]; then
    echo "HADOOP_PREFIX needs to be set!"
    exit 1
  fi
  if [ ! -d "$HADOOP_PREFIX" ]; then
    echo "HADOOP_PREFIX=$HADOOP_PREFIX is not a valid directory.  Please correct it in your .bashrc or fluo-env.sh"
    exit 1
  fi
}

function setup_service {
  if [[ "$@" =~ ^.*-a\ *([^\ ]*).*$ ]]; then
    app=${BASH_REMATCH[1]}
    verify_app "$app"
    check_conn_props
    # create a temp dir to fetch application jars to
    app_lib=$(mktemp -d "$FLUO_TMP"/fluo-"$app"-XXXXXXXXX) || die "fatal: unable to allocate a temporary directory"
    # schedule removal of app_lib tmp dir when this script exits
    trap "rm -rf '""$app_lib""'" EXIT HUP INT QUIT TERM
    $JAVA org.apache.fluo.command.FluoGetJars -d "$app_lib" "$@"
    export CLASSPATH="$conf:$app_lib/*:$CLASSPATH"
  else
    echo "Application name must be set!"
    print_usage
    exit 1
  fi
}

case "$1" in
config)
  check_conn_props
  $JAVA org.apache.fluo.command.FluoConfig "${@:2}"
  ;;
get-jars)
  check_conn_props
  $JAVA org.apache.fluo.command.FluoGetJars "${@:2}"
  ;;
init)
  if [ -f "$FLUO_CONN_PROPS" ]; then
    if [[ $2 = *"-h"* ]]; then
      $JAVA org.apache.fluo.command.FluoInit -h
      exit 0
    fi
    init_dir=$($JAVA org.apache.fluo.command.FluoInit "${@:2}" --retrieveProperty fluo.observer.init.dir)
    if [ -d "$init_dir" ]; then
      echo "Adding $init_dir/* to CLASSPATH"
      export CLASSPATH="$init_dir/*:$CLASSPATH"
    fi
    $JAVA org.apache.fluo.command.FluoInit "${@:2}"
  else
    deprecated_verify_full "$2"
    check_hadoop
    export CLASSPATH="$APP_LIB_DIR/*:$CLASSPATH"
    java org.apache.fluo.cluster.command.FluoCommand "$basedir" "$HADOOP_PREFIX" "$@"
  fi
  ;;
remove)
  if [[ $2 = *"-h"* ]]; then
    $JAVA org.apache.fluo.command.FluoRemove -h
    exit 0
  fi
  $JAVA org.apache.fluo.command.FluoRemove "${@:2}"
  ;;
oracle)
  if [[ $2 = *"-h"* ]]; then
    $JAVA org.apache.fluo.command.FluoOracle -h
    exit 0
  fi
  setup_service "${@:2}"
  $JAVA org.apache.fluo.command.FluoOracle "${@:2}"
  ;;
worker)
  if [[ $2 = *"-h"* ]]; then
    $JAVA org.apache.fluo.command.FluoWorker -h
    exit 0
  fi
  setup_service "${@:2}"
  $JAVA org.apache.fluo.command.FluoWorker "${@:2}"
  ;;
scan)
  if [ -f "$FLUO_CONN_PROPS" ]; then
    $JAVA org.apache.fluo.command.FluoScan "${@:2}"
  else
    check_hadoop
    java org.apache.fluo.cluster.command.FluoCommand "$basedir" "$HADOOP_PREFIX" "$@"
  fi
  ;;
ps)
  jps -m | grep Fluo
  ;;
list)
  if [ -f "$FLUO_CONN_PROPS" ]; then
    $JAVA org.apache.fluo.command.FluoList "${@:2}"
  else
    check_hadoop
    java org.apache.fluo.cluster.command.FluoCommand "$basedir" "$HADOOP_PREFIX" list app "${@:2}"
  fi
  ;;
classpath)
  echo "$CLASSPATH"
  ;;
exec)
  if [ -f "$FLUO_CONN_PROPS" ]; then
    app=$2
    verify_app "$app"
    check_conn_props
    # create a temp dir to fetch application jars to
    app_lib=$(mktemp -d "$FLUO_TMP"/fluo-"$app"-XXXXXXXXX) || die "fatal: unable to allocate a temporary directory"
    # schedule removal of app_lib tmp dir when this script exits
    trap "rm -rf '""$app_lib""'" EXIT HUP INT QUIT TERM
    $JAVA org.apache.fluo.command.FluoGetJars -d "$app_lib" -a "$app"
    export CLASSPATH="$conf:$app_lib/*:$CLASSPATH"
    $JAVA org.apache.fluo.command.FluoExec "${@:2}"
  else
    deprecated_verify "$2"
    export CLASSPATH="$APP_LIB_DIR/*:$CLASSPATH"
    java org.apache.fluo.cluster.command.FluoCommand "$basedir" "$HADOOP_PREFIX" "$@"
  fi
  ;;
status)
  if [ -f "$FLUO_CONN_PROPS" ]; then
    $JAVA org.apache.fluo.command.FluoStatus "${@:2}"
  else
    check_hadoop
    java org.apache.fluo.cluster.command.FluoCommand "$basedir" "$HADOOP_PREFIX" "$@"
  fi
  ;;
version)
  echo "$FLUO_VERSION"
  ;;
wait)
  if [ -f "$FLUO_CONN_PROPS" ]; then
    $JAVA org.apache.fluo.command.FluoWait "${@:2}"
  else
    deprecated_verify "$2"
    export CLASSPATH="$APP_LIB_DIR/*:$CLASSPATH"
    java org.apache.fluo.cluster.command.FluoCommand "$basedir" "$HADOOP_PREFIX" "$@"
  fi
  ;;
# Commands below this comment are deprecated
new)
  deprecated_verify "$2"
  java org.apache.fluo.cluster.util.ValidateAppName "$APP"
  if [ -d "$APP_DIR" ]; then
    echo "The Fluo '$APP' application already has a directory in apps/"
    exit 1
  fi
  mkdir -p "$APP_DIR"
  mkdir -p "$APP_CONF_DIR"
  mkdir -p "$APP_LIB_DIR"
  copy_config fluo.properties
  if [[ "$OSTYPE" == "darwin"* ]]; then
    sed_cmd="sed -i .bak"
  else
    sed_cmd="sed -i"
  fi
  $sed_cmd "s/fluo.client.application.name=/fluo.client.application.name=$APP/g" "$APP_CONF_DIR/fluo.properties"
  copy_config logback.xml
  ;;
start)
  deprecated_verify_full "$2"
  check_hadoop
  export CLASSPATH="$APP_LIB_DIR/*:$CLASSPATH"
  java org.apache.fluo.cluster.command.FluoCommand "$basedir" "$HADOOP_PREFIX" "$@"
  ;;
stop)
  check_hadoop
  java org.apache.fluo.cluster.command.FluoCommand "$basedir" "$HADOOP_PREFIX" "$@"
  ;;
kill|info)
  deprecated_verify "$2"
  check_hadoop
  java org.apache.fluo.cluster.command.FluoCommand "$basedir" "$HADOOP_PREFIX" "$@"
  ;;
*)
  print_usage
esac
