#!/bin/bash

#---------------------------------------------------
# SCRIPT CONSTANTS DECLARATION
#---------------------------------------------------
DEFAULT_LANGUAGE=java

DEFAULT_SUMMARY=false

DEFAULT_LOG_LEVEL=off
DEFAULT_LOG_LEVEL_ARGUMENT=debug
LOG_LEVEL_DEBUG=debug
LOG_LEVEL_INFO=info
LOG_LEVEL_OFF=off
DEFAULT_EXTRAE_CONFIG_FILE="null"

DEFAULT_TASK_EXECUTION=compss
DEFAULT_GRAPH=false
DEFAULT_GRAPH_ARGUMENT=true

DEFAULT_TRACING=false
DEFAULT_TRACING_ARGUMENT=true # 0: activated, 1: basic, 2:advanced

DEFAULT_MONITORING_INTERVAL=0
DEFAULT_MONITORING_INTERVAL_ARGUMENT=2000

DEFAULT_TASK_COUNT=50
DEFAULT_CLASSPATH=$(pwd)
DEFAULT_PYTHONPATH=$(pwd)
DEFAULT_LIBRARY_PATH=$(pwd)
DEFAULT_APPDIR=$(pwd)
DEFAULT_STORAGE_CONF="None"
DEFAULT_PyOBJECT_SERIALIZE=false

DEFAULT_COMMUNICATION_ADAPTOR=es.bsc.compss.nio.master.NIOAdaptor
#DEFAULT_COMMUNICATION_ADAPTOR=es.bsc.compss.gat.master.GATAdaptor
DEFAULT_CONNECTOR=es.bsc.compss.connectors.DefaultSSHConnector
DEFAULT_MASTER_PORT="[43000,44000]"
DEFAULT_MASTER_NAME=""
#DEFAULT_SCHEDULER=es.bsc.compss.scheduler.fullGraphScheduler.FullGraphScheduler
#DEFAULT_SCHEDULER=es.bsc.compss.scheduler.fifoScheduler.FIFOScheduler
#DEFAULT_SCHEDULER=es.bsc.compss.scheduler.fifoDataScheduler.FIFODataScheduler
#DEFAULT_SCHEDULER=es.bsc.compss.components.impl.TaskScheduler
DEFAULT_SCHEDULER=es.bsc.compss.scheduler.loadBalancingScheduler.LoadBalancingScheduler
RUNTIME_LOADER=es.bsc.compss.loader.ITAppLoader
DEFAULT_JVM_MASTER=""
DEFAULT_JVM_WORKERS="-Xms1024m,-Xmx1024m,-Xmn400m"
DEFAULT_CPU_AFFINITY="automatic" # disabled, automatic, user string
DEFAULT_GPU_AFFINITY="automatic" # disabled, automatic, user string

DEFAULT_DEBUGGER=false
DEFAULT_DEBUGGER_ARGUMENT=true
DEFAULT_DEBUGGER_PORT=9999

DEFAULT_PERSISTENT_WORKER_C=false
DEFAULT_EXTERNAL_ADAPTATION=false
APPLICATION_ERROR="Error: Application name not provided"
LANGUAGE_ERROR="Value of option --lang must be: java, c or python"
JAVA_HOME_ERROR="Error: Cannot find Java JRE installation. Please set JAVA_HOME."
JAVA_JRE_ERROR="Error: Can't find JVM libraries in JAVA_HOME. Please check your Java JRE Installation."
RUNTIME_ERROR="Error running application"
TMP_FILE_JVM_ERROR="Error: Can't create temporary file for JVM options."
TRACING_ERROR="Value of option --tracing must be: false, true, basic or advanced"

#---------------------------------------------------
# FUNCTIONS DECLARATION
#---------------------------------------------------
show_opts() {
  cat <<EOT

  Tools enablers:
    --graph=<bool>, --graph, -g             Generation of the complete graph (true/false)
                                            When no value is provided it is set to ${DEFAULT_GRAPH_ARGUMENT}
                                            Default: ${DEFAULT_GRAPH}
    --tracing=<level>, --tracing, -t        Set generation of traces and/or tracing level ( [ true | basic ] | advanced | false)
                                            True and basic levels will produce the same traces.
                                            When no value is provided it is set to ${DEFAULT_TRACING_ARGUMENT}
                                            Default: ${DEFAULT_TRACING}
    --monitoring=<int>, --monitoring, -m    Period between monitoring samples (milliseconds)
                                            When no value is provided it is set to ${DEFAULT_MONITORING_INTERVAL_ARGUMENT}
                                            Default: ${DEFAULT_MONITORING_INTERVAL}
    --external_debugger=<int>,
    --external_debugger		            Enables external debugger connection on the specified port (or ${DEFAULT_DEBUGGER_PORT} if empty)
                                            Default: ${DEFAULT_DEBUGGER}

  Runtime configuration options:
    --task_execution=<compss|storage>	    Task execution under COMPSs or Storage.
                                            Default: ${DEFAULT_TASK_EXECUTION}
    --storage_conf=<path>		    Path to the storage configuration file
					    Default: ${DEFAULT_STORAGE_CONF}
    --project=<path>                        Path to the project XML file
                                            Default: ${DEFAULT_PROJECT}
    --resources=<path>                      Path to the resources XML file
                                            Default: ${DEFAULT_RESOURCES}
    --lang=<name>                           Language of the application (java/c/python)
                                            Default: Inferred is possible. Otherwise: ${DEFAULT_LANGUAGE}
    --summary                               Displays a task execution summary at the end of the application execution
                                            Default: ${DEFAULT_SUMMARY}
    --log_level=<level>, --debug, -d        Set the debug level: ${LOG_LEVEL_OFF} | ${LOG_LEVEL_INFO} | ${LOG_LEVEL_DEBUG}
                                            Default: ${DEFAULT_LOG_LEVEL}

  Advanced options:
    --extrae_config_file=<path>             Sets a custom extrae config file. Must be in a shared disk between all COMPSs workers.
                                            Default: ${DEFAULT_EXTRAE_CONFIG_FILE}
    --comm=<ClassName>                      Class that implements the adaptor for communications
                                            Supported adaptors: es.bsc.compss.nio.master.NIOAdaptor | es.bsc.compss.gat.master.GATAdaptor
                                            Default: ${DEFAULT_COMMUNICATION_ADAPTOR}
    --conn=<className>                      Class that implements the runtime connector for the cloud
                                            Supported connectors: es.bsc.compss.connectors.DefaultSSHConnector
								  | es.bsc.compss.connectors.DefaultNoSSHConnector
                                            Default: ${DEFAULT_CONNECTOR}
    --scheduler=<className>	            Class that implements the Scheduler for COMPSs
                                            Supported schedulers: es.bsc.compss.scheduler.fullGraphScheduler.FullGraphScheduler
                                                                  | es.bsc.compss.scheduler.fifoScheduler.FIFOScheduler
                                                                  | es.bsc.compss.scheduler.resourceEmptyScheduler.ResourceEmptyScheduler
                                            Default: ${DEFAULT_SCHEDULER}
    --scheduler_config_file=<path>          Path to the file which contains the scheduler configuration.
                                            Default: Empty
    --library_path=<path>                   Non-standard directories to search for libraries (e.g. Java JVM library, Python library, C binding library)
                                            Default: Working Directory
    --classpath=<path>                      Path for the application classes / modules
                                            Default: Working Directory
    --appdir=<path>			    Path for the application class folder.
					    Default: ${DEFAULT_APPDIR}
    --pythonpath=<path>                     Additional folders or paths to add to the PYTHONPATH
                                            Default: ${DEFAULT_PYTHONPATH}
    --base_log_dir=<path>                   Base directory to store COMPSs log files (a .COMPSs/ folder will be created inside this location)
                                            Default: User home
    --specific_log_dir=<path>               Use a specific directory to store COMPSs log files (the folder MUST exist and no sandbox is created)
					    Warning: Overwrites --base_log_dir option
                                            Default: Disabled
    --uuid=<int>                            Preset an application UUID
                                            Default: Automatic random generation
    --master_name=<string>                  Hostname of the node to run the COMPSs master
                                            Default: ${DEFAULT_MASTER_NAME}
    --master_port=<int>                     Port to run the COMPSs master communications.
                                            Only for NIO adaptor
                                            Default: ${DEFAULT_MASTER_PORT}
    --jvm_master_opts="<string>"            Extra options for the COMPSs Master JVM. Each option separed by "," and without blank spaces (Notice the quotes)
                                            Default: ${DEFAULT_JVM_MASTER}
    --jvm_workers_opts="<string>"	    Extra options for the COMPSs Workers JVMs. Each option separed by "," and without blank spaces (Notice the quotes)
                                            Default: ${DEFAULT_JVM_WORKERS}
    --cpu_affinity="<string>"               Sets the CPU affinity for the workers
                                            Supported options: disabled, automatic, user defined map of the form "0-8/9,10,11/12-14,15,16"
                                            Default: ${DEFAULT_CPU_AFFINITY}
    --gpu_affinity="<string>"               Sets the GPU affinity for the workers
                                            Supported options: disabled, automatic, user defined map of the form "0-8/9,10,11/12-14,15,16"
                                            Default: ${DEFAULT_GPU_AFFINITY}
    --task_count=<int>                      Only for C/Python Bindings. Maximum number of different functions/methods, invoked from the application, that have been selected as tasks
                                            Default: ${DEFAULT_TASK_COUNT}
    --input_profile=<path>                  Path to the file which stores the input application profile
                                            Default: Empty
    --output_profile=<path>                 Path to the file to store the application profile at the end of the execution
                                            Default: Empty 
    --PyObject_serialize=<bool>             Only for Python Binding. Enable the object serialization to string when possible (true/false).
                                            Default: $DEFAULT_PyOBJECT_SERIALIZE
    --persistent_worker_c=<bool>            Only for C Binding. Enable the persistent worker in c (true/false).
                                            Default: $DEFAULT_PERSISTENT_WORKER_C
    --enable_external_adaptation=<bool>     Enable external adaptation. This option will disable the Resource Optimizer.
                                            Default: false

* Application name:
    For Java applications:   Fully qualified name of the application
    For C applications:      Path to the master binary
    For Python applications: Path to the .py file containing the main program

* Application arguments:
    Command line arguments to pass to the application. Can be empty.

EOT
}

usage() {
  exitValue=$1

  cat <<EOT
Usage: $0 [options] application_name application_arguments

* Options:
  General:
    --help, -h                              Print this help message

    --opts                                  Show available options

    --version, -v 			    Print COMPSs version
EOT
  show_opts
  exit "$exitValue"
}

show_version() {
  echo "COMPSs version 2.2 Camellia"
  echo " "
}

show_full_version() {
  cat << EOF
        .-~~-.--.            ____        ____ 
       :         )          |___ \      |___ \\
 .~ ~ -.\       /.- ~~ .      __) |       __) |
 >       \`.   .'       <     / __/   _   / __/
(         .- -.         )   |_____| |_| |_____|
 \`- -.-~  \`- -'  ~-.- -'
   (        :        )           _ _ .-:
    ~--.    :    .--~        .-~  .-~  }
        ~-.-^-.-~ \_      .~  .-~   .~
                 \ \'     \ '_ _ -~
                  \`.\`.    //
         . - ~ ~-.__\`.\`-.//
     .-~   . - ~  }~ ~ ~-.~-.
   .' .-~      .-~       :/~-.~-./:
  /_~_ _ . - ~                 ~-.~-._
                                   ~-.<
EOF
  show_version
}

show_flower() {
  cat << EOF

Camellia (Camellia japonica) - Flower

Camellia japonica is a flowering tree or shrub, usually 1.5–6 metres tall. 
In the wild, flowering is between January and March. The flowers appear
along the branches, particularly towards the ends, and have very short
stems. They occur either alone or in pairs, and are 6–10 centimetres 
across. There are about nine greenish bracteoles and sepals. 

Flowers of the wild species have six or seven rose or white petals, each
3–4.5 centimetres long by 1.5–2.5 centimetres wide. The numerous stamens 
are 2.5–3.5 centimetres long, the outer whorl being joined at the base.
The fruit consists of a globe-shaped capsule with three compartments 
(locules), each with one or two large brown seeds. Fruiting occurs in 
September to October in the wild.

Check more information:
   https://en.wikipedia.org/wiki/Camellia_japonica

EOF
}

show_recipe() {
  cat << EOF

Camellia - Cocktail

  30 ml of Geen Tea
  30 ml of White Rum
  15 ml of Limoncello
  1 barspoon of Elderflower
  1 barspoon of Honey

EOF
}

# Displays parsing arguments errors
display_error() {
  local error_msg=$1

  echo "$error_msg"
  echo " "

  usage 1
}

# Displays runtime/application errors
error_msg() {
  local error_msg=$1

  # Display error
  echo
  echo "$error_msg"
  echo

  # Exit
  exit 1
}

check_env() {
  # Added for SGE queue systems which do not allow to copy LD_LIBRARY_PATH
  if [ -z "$LD_LIBRARY_PATH" ]; then
     if [ -n "$LIBRARY_PATH" ]; then
         export LD_LIBRARY_PATH=$LIBRARY_PATH
         echo "[  INFO] LD_LIBRARY_PATH not defined set to LIBRARY_PATH"
     fi
  fi
  
  # Command to simulate realpath
  COMPSS_HOME="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../../.. && pwd )"
  export COMPSS_HOME=${COMPSS_HOME}

  if [ -z "$DEFAULT_PROJECT" ]; then
    DEFAULT_PROJECT=${COMPSS_HOME}/Runtime/configuration/xml/projects/default_project.xml
  fi

  if [ -z "$DEFAULT_RESOURCES" ]; then
    DEFAULT_RESOURCES=${COMPSS_HOME}/Runtime/configuration/xml/resources/default_resources.xml
  fi

  if [ -z "${GAT_LOCATION}" ]; then
    GAT_LOCATION=${COMPSS_HOME}/Dependencies/JAVA_GAT
  fi

  if [ -z "${EXTRAE_HOME}" ]; then
    EXTRAE_HOME=${COMPSS_HOME}/Dependencies/extrae
  fi

  if [ -z "${EXTRAE_LIB}" ]; then
    EXTRAE_LIB=${EXTRAE_HOME}/lib
  fi

  if [[ -z "$JAVA_HOME" ]]; then
    JAVA=java
  elif [ -f "$JAVA_HOME"/jre/bin/java ]; then
    JAVA=$JAVA_HOME/jre/bin/java
  elif [ -f "$JAVA_HOME"/bin/java ]; then
    JAVA=$JAVA_HOME/bin/java
  else
    display_error "${JAVA_HOME_ERROR}"
  fi
}

get_args() {
  # Parse COMPSs Options
  while getopts hvgtmd-: flag; do
    # Treat the argument
    case "$flag" in
      h)
	# Display help
	usage 0
	;;
      v)
        # Display version
        show_version
        exit
        ;;
      g)
	# Enable graph generation at the end of the execution
	graph=${DEFAULT_GRAPH_ARGUMENT}
	;;
      t)
	# Enable tracing
	tracing=${DEFAULT_TRACING_ARGUMENT}
	;;
      m)
	# Enable monitoring with default value
	monitoring=${DEFAULT_MONITORING_INTERVAL_ARGUMENT}
	;;
      d)
        # Enable debug in log level
        log_level=${DEFAULT_LOG_LEVEL_ARGUMENT}
        ;;
      -)
	# Check more complex arguments
	case "$OPTARG" in
	  help)
	    # Display help
	    usage 0
	    ;;
          version)
            # Show version
            show_full_version
            exit 0
            ;;
          opts)
            # Display help
            show_opts
            exit 0
            ;;
          flower)
            # Display flower
            show_flower
            exit 0
            ;;
          recipe)
            # Display recipe
            show_recipe
            exit 0
            ;;
	  project=*)
	    # Custom project file
	    projFile=${OPTARG//project=/}
	    if [ ! -f "$projFile" ]; then
	      echo "[ WARNING ]: Project XML file '$projFile' could not be found."
	    fi
	    ;;
	  resources=*)
	    # Custom resources file
	    resFile=${OPTARG//resources=/}
	    if [ ! -f "$resFile" ]; then
	      echo "[ WARNING ]: Resources XML file '$resFile' could not be found."
	    fi
            ;;
          summary)
            summary=true
            ;;
	  storage_conf=*)
            storageConf=${OPTARG//storage_conf=/}
            ;;
	  task_execution=*)
	    # Task execution under COMPSs or Storage
	    taskExecution=${OPTARG//task_execution=/}
	    ;;
	  lang=*)
	    # Language selection
	    lang=${OPTARG//lang=/}
	    ;;
          log_level=*)
            # Enable different log_levels by user selection
            log_level=${OPTARG//log_level=/}
            ;;
          debug*)
            # Enable debug in log level
            log_level=${DEFAULT_LOG_LEVEL_ARGUMENT}
            ;;
	  graph=*)
	    # Graph generation at the end of the execution with user option
	    graph=${OPTARG//graph=/}
	    ;;
	  graph)
	    # Graph generation at the end of the execution by default arg
	    graph=${DEFAULT_GRAPH_ARGUMENT}
	    ;;
          tracing=*)
            # Tracing system
            tracing=${OPTARG//tracing=/}
            ;;
          tracing)
            # Tracing system
            tracing=${DEFAULT_TRACING_ARGUMENT}
            ;;
          extrae_config_file=*)
            # Custom extrae config file
            custom_extrae_config_file=${OPTARG//extrae_config_file=/}
            ;;
	  monitoring=*)
	    # Value between monitor steps (ms)
	    monitoring=${OPTARG//monitoring=/}
	    ;;
	  monitoring)
	    # Value between monitor steps (ms)
	    monitoring=${DEFAULT_MONITORING_INTERVAL_ARGUMENT}
	    ;;
	  comm=*)
	    # Communication adaptor main class
	    comm=${OPTARG//comm=/}
	    ;;
	  conn=*)
	    # Communication connector main class
	    conn=${OPTARG//conn=/}
	    ;;
          scheduler=*)
            # Scheduler main class
            scheduler=${OPTARG//scheduler=/}
            ;;
	  classpath=*)
	    # Additional classpath
	    cp=${OPTARG//classpath=/}
	    ;;
	  library_path=*)
	    # Additional library path
	    library_path=${OPTARG//library_path=/}
	    ;;
          jvm_master_opts=*)
            # Master JVM option
            jvm_master_opts=${OPTARG//jvm_master_opts=/}
            ;;
          jvm_workers_opts=*)
            # Workers JVMs option
            jvm_workers_opts=${OPTARG//jvm_workers_opts=/}
            ;;
          cpu_affinity=*)
            # Worker CPU affinity
            worker_cpu_affinity=${OPTARG//cpu_affinity=/}
            ;;
          gpu_affinity=*)
            # Worker GPU affinity
            worker_gpu_affinity=${OPTARG//gpu_affinity=/}
            ;;
          external_debugger=*)
            # Enable external debugger on specific port
            external_debugger=${DEFAULT_DEBUGGER_ARGUMENT}
            external_debugger_port=${OPTARG//external-debugger=/}
            ;;
          external_debugger)
            # Enable default external debugger
            external_debugger=${DEFAULT_DEBUGGER_ARGUMENT}
            external_debugger_port=${DEFAULT_DEBUGGER_PORT}
            ;;
          base_log_dir=*)
            # Set a custom base log dir
            base_log_dir=${OPTARG//base_log_dir=/}
            ;;
          specific_log_dir=*)
            # Set a custom specific log dir
            specific_log_dir=${OPTARG//specific_log_dir=/}
            ;;
	  uuid=*)
            # Preset an Application UUID
            uuid=${OPTARG//uuid=/}
            ;;
          master_name=*)
            # Preset a Master hostname
            master_name=${OPTARG//master_name=/}
            ;;
          master_port=*)
            # Preset a Master port
            master_port=${OPTARG//master_port=/}
            ;;
          task_count=*)
            # Maximum task count. Only for bindings
            task_count=${OPTARG//task_count=/}
            ;;
    	  appdir=*)
	    # Main binary directory (only for C/C++ applications)
	    task_count=${OPTARG//appdir=/}
	    ;;
          pythonpath=*)
            # Additional pythonpath
            pythonpath=${OPTARG//pythonpath=/}
            ;;
          PyObject_serialize=*)
            # Enable the object to string serialization (only for PyCOMPSs applications)
            PyObject_serialize=${OPTARG//PyObject_serialize=/}
            ;;
          persistent_worker_c=*)
            # Enable the persistent worker for C binding (only for C-binding applications)
            persistent_worker_c=${OPTARG//persistent_worker_c=/}
            ;;
          input_profile=*)
            # Specify the file where there is stored the tasks profiles
            input_profile=${OPTARG//input_profile=/}
            ;;
          output_profile=*)
            # Specify the file where COMPSs will store the tasks profile
            output_profile=${OPTARG//output_profile=/}
            ;;
          scheduler_config_file=*)
            # Specify the file where COMPSs will store the tasks profile
            scheduler_config=${OPTARG//scheduler_config_file=/}
            ;;
          enable_external_adaptation=*)
            # Enable the external adaptation (disables de Resource Optimizer) 
            external_adaptation=${OPTARG//enable_external_adaptation=/}
            ;;
	  *)
	    # Flag didn't match any patern. Raise exception
	    display_error "Bad argument: $OPTARG"
	    ;;
	esac
	;;
      *)
	# Flag didn't match any patern. End of COMPSs flags
	break
	;;
    esac
  done
  # Shift COMPSs arguments
  shift $((OPTIND-1))

  # Parse application name
  if [[ $# -eq 0 ]]; then
    display_error "${APPLICATION_ERROR}"
  else
    fullAppPath=$1
    if [ -z "$fullAppPath" ]; then
      display_error "${APPLICATION_ERROR}"
    else
      shift 1
    fi
  fi

  # Parse application arguments
  application_args=$*
}

###############################################
# Infers the language from the Application Path
# WARN: Sets global lang variable
###############################################
infer_language() {
  local fullApp
  local fileName
  local extension
  local isPython
  local isPythonCompiled
  local isC

  fullApp=$1                                                                                                                                                                                                                                                                 

  fileName=$(basename "$fullApp")
  extension=${fileName##*.}
  if [ "$extension" == "py" ] || [ "$extension" == "pyc" ]; then
    echo "[  INFO] Inferred PYTHON language"
    lang=python
  else
    # Try to infer language by file command
    isPython=$(file "$fullAppPath" | grep "Python" | cat)
    isPythonCompiled=$(file "$fullAppPath" | grep "python" | cat)
    isC=$(file "$fullAppPath" | grep "executable" | cat)
    if [ -n "$isPython" ] || [ -n "$isPythonCompiled" ]; then
      echo "[  INFO] Inferred PYTHON language"
      lang=python
    elif [ -n "$isC" ]; then
      echo "[  INFO] Inferred C/C++ language"
      lang=c
    else
      # Lang cannot be infered or it's the default JAVA
      echo "[  INFO] Using default language: ${DEFAULT_LANGUAGE}"
      lang=${DEFAULT_LANGUAGE}
    fi
  fi
}

check_args() {
  if [ -z "${taskExecution}" ]; then
    echo "[  INFO] Using default execution type: ${DEFAULT_TASK_EXECUTION}"
    taskExecution=${DEFAULT_TASK_EXECUTION}
  fi

  if [ -z "$projFile" ]; then
    echo "[  INFO] Using default location for project file: ${DEFAULT_PROJECT}"
    projFile=${DEFAULT_PROJECT}
  fi

  if [ -z "$resFile" ]; then
    echo "[  INFO] Using default location for resources file: ${DEFAULT_RESOURCES}"
    resFile=${DEFAULT_RESOURCES}
  fi

  if [ -z "${summary}" ]; then
    summary=${DEFAULT_SUMMARY}
  fi

  if [ -z "$lang" ]; then
    # Try to infer language
    infer_language "$fullAppPath"
  elif [ "$lang" = "java" ]; then
    lang=java
  elif [ "$lang" = "c" ]; then
    lang=c
  elif [ "$lang" = "python" ]; then
    lang=python
  else
    display_error "${LANGUAGE_ERROR}"
  fi

  if [ -z "${log_level}" ]; then
    log_level=${DEFAULT_LOG_LEVEL}
  fi

  if [ -z "$graph" ]; then
    graph=${DEFAULT_GRAPH}
  fi

  # TRACING file option
  if [ -z "${custom_extrae_config_file}" ]; then
    custom_extrae_config_file=${DEFAULT_EXTRAE_CONFIG_FILE}
  fi

  # Tracing level
  if [ -z "$tracing" ]; then
    tracing=0
  elif [ $tracing == "false" ]; then
     tracing=0
  elif [ $tracing == "basic" ] || [ $tracing == "true" ]; then
    tracing=1
  elif [ $tracing == "advanced" ]; then
    tracing=2
  else
    display_error "${TRACING_ERROR}"
  fi
  if [ $tracing -eq 1 ]; then
    extraeFile=${COMPSS_HOME}/Runtime/configuration/xml/tracing/extrae_basic.xml
  elif [ $tracing -eq 2 ]; then
    extraeFile=${COMPSS_HOME}/Runtime/configuration/xml/tracing/extrae_advanced.xml
  fi

  # Overwrite extraeFile if already defined
  if [ "${custom_extrae_config_file}" != "${DEFAULT_EXTRAE_CONFIG_FILE}" ]; then
    extraeFile=${custom_extrae_config_file}
  fi

  # Set tracing env
  if [ $tracing -gt 0 ]; then
    export EXTRAE_HOME=${EXTRAE_HOME}
    export LD_LIBRARY_PATH=${EXTRAE_LIB}:${LD_LIBRARY_PATH}
    export EXTRAE_CONFIG_FILE=${extraeFile}
  fi

  if [ -z "$monitoring" ]; then
    monitoring=${DEFAULT_MONITORING_INTERVAL}
  else
    # If monitor as been activated trigger final graph generation and log_level = at least info
    graph=${DEFAULT_GRAPH_ARGUMENT}
    if [ "${log_level}" == "${DEFAULT_LOG_LEVEL}" ] || [ "${log_level}" == "${LOG_LEVEL_OFF}" ]; then
       log_level=${LOG_LEVEL_INFO}
    fi
  fi

  # Storage configuration
  if [ -z "$storageConf" ]; then
    storageConf="null"
  fi 

  # Master log level
  if [ "${log_level}" == "${DEFAULT_LOG_LEVEL}" ]; then
    itlog4j_file="COMPSsMaster-log4j"
  else
    itlog4j_file="COMPSsMaster-log4j.${log_level}"
  fi

  # Adaptor
  if [ -z "$comm" ]; then
    comm=${DEFAULT_COMMUNICATION_ADAPTOR}
  fi
  
  # Connector
  if [ -z "$conn" ]; then
    conn=${DEFAULT_CONNECTOR}
  fi

  # Scheduler
  if [ -z "$scheduler" ]; then
    scheduler=${DEFAULT_SCHEDULER}
  fi

  # Classpath
  if [ -z "$cp" ]; then
    cp=${DEFAULT_CLASSPATH}
    for jar in "${DEFAULT_CLASSPATH}"/*.jar; do
       cp=$cp:$jar
    done
  else
    fcp=""
    for currcp in ${cp//:/$'\n'}; do
      if [ ! "${currcp:0:1}" == '/' ]; then # Relative paths to absolute
        if [ -d "$currcp" ] || [ -f "$currcp" ]; then   # If the dir/file exists locally
	  absdir="$(cd "$(dirname "$currcp")" && pwd)" # Get absolute dir
	  file="$(basename "$currcp")"
	  fcp="${fcp}$absdir/$file:"
	else
	  echo "[ WARNING ]: Classpath \"$currcp\" does not exist..."
	fi
      else
   	fcp="${fcp}$currcp:"
      fi
    done
    cp="$fcp"
    echo "[  INFO] Relative Classpath resolved: $cp"
  fi
  export CLASSPATH=$cp:$CLASSPATH

  if [ -z "$task_count" ]; then
    task_count=${DEFAULT_TASK_COUNT}
  fi

  if [ -z "$library_path" ]; then
    library_path=${DEFAULT_LIBRARY_PATH}
  fi
  export LD_LIBRARY_PATH=$library_path:$LD_LIBRARY_PATH

  if [ -z "$uuid" ]; then
    uuid=$(uuidgen)
    if [ -z "$uuid" ]; then
      uuid=$(cat /proc/sys/kernel/random/uuid)
    fi
  fi

  # MASTER
  if [ -z "${master_name}" ]; then
    master_name=${DEFAULT_MASTER_NAME}
  fi

  # JVM
  if [ -z "${jvm_master_opts}" ] || [ "${jvm_master_opts}" = \"\" ]; then
    jvm_master_opts=${DEFAULT_JVM_MASTER}
  fi
  # Change jvm master opts separation character "," by " "
  jvm_master_opts=$(echo $jvm_master_opts | tr "," "\n")

  if [ -z "${jvm_workers_opts}" ] || [ "${jvm_workers_opts}" = \"\" ]; then
    jvm_workers_opts=${DEFAULT_JVM_WORKERS}
  fi

  # WORKER THREAD AFFINITY
  if [ -z "${worker_cpu_affinity}" ]; then
    worker_cpu_affinity=${DEFAULT_CPU_AFFINITY}
  fi
  if [ -z "${worker_gpu_affinity}" ]; then
    worker_gpu_affinity=${DEFAULT_GPU_AFFINITY}
  fi

  # Debugger
  if [ -z "$external_debugger" ]; then
    external_debugger=${DEFAULT_DEBUGGER}
  fi
  if [ "${external_debugger}" == "true" ]; then
    jvm_debugger_opts="-Xdebug -agentlib:jdwp=transport=dt_socket,address=${external_debugger_port},server=y,suspend=y"
  else
    jvm_debugger_opts=""
  fi

  # Options ONLY for PYTHON
  if [ "$lang" = "python" ]; then
    if [ -z "$pythonpath" ]; then
      pythonpath=${DEFAULT_PYTHONPATH}
    else
      # Adds execution dir by default to pythonpath
      pythonpath=$pythonpath":${DEFAULT_PYTHONPATH}"
    fi
    # Adds all given Pythonpaths to the global variable
    export PYTHONPATH=$pythonpath:$PYTHONPATH

    if [ -z "$PyObject_serialize" ]; then
      PyObject_serialize=$DEFAULT_PyOBJECT_SERIALIZE
    fi
  fi

  # Options ONLY for C
  if [ "$lang" = "c" ]; then
    if [ -z "$appdir" ]; then
      appdir=$DEFAULT_APPDIR
    fi
  fi
  if [ -z "$persistent_worker_c" ]; then
	persistent_worker_c=$DEFAULT_PERSISTENT_WORKER_C
  fi
  if [ -z "$external_adaptation" ]; then
	external_adaptation=$DEFAULT_EXTERNAL_ADAPTATION
  fi
}

execute_runtime() {
  # Create tmp dir for initial loggers configuration
  mkdir /tmp/"$uuid"

  # Create JVM Options file
  appName=$(basename "$fullAppPath")
  generate_jvm_opts_file

  # Init COMPSs
  echo -e "\n----------------- Executing $appName --------------------------\n"
  if [ $lang = java ]; then
    add_jvm_opts_java
    exec_java
  elif [ $lang = c ]; then
    add_jvm_opts_c
    exec_c
  elif [ $lang = python ]; then
    add_jvm_opts_python
    exec_python
  fi
  echo
  echo ------------------------------------------------------------
}

generate_jvm_opts_file() {
  jvm_options_file=$(mktemp) || display_error "${TMP_FILE_JVM_ERROR}"

  cat >> "${jvm_options_file}" << EOT
${jvm_master_opts}
${jvm_debugger_opts}
-XX:+PerfDisableSharedMem
-XX:-UsePerfData
-XX:+UseG1GC
-XX:+UseThreadPriorities
-XX:ThreadPriorityPolicy=42
-Dlog4j.configurationFile=${COMPSS_HOME}/Runtime/configuration/log/${itlog4j_file}
-Dcompss.to.file=false
-Dcompss.project.file=${projFile}
-Dcompss.resources.file=${resFile}
-Dcompss.project.schema=${COMPSS_HOME}/Runtime/configuration/xml/projects/project_schema.xsd
-Dcompss.resources.schema=${COMPSS_HOME}/Runtime/configuration/xml/resources/resources_schema.xsd
-Dcompss.lang=${lang}
-Dcompss.summary=${summary}
-Dcompss.task.execution=$taskExecution
-Dcompss.storage.conf=$storageConf
-Dcompss.core.count=${task_count}
-Dcompss.appName=${appName}
-Dcompss.uuid=${uuid}
-Dcompss.baseLogDir=${base_log_dir}
-Dcompss.specificLogDir=${specific_log_dir}
-Dcompss.appLogDir=/tmp/${uuid}/
-Dcompss.graph=${graph}
-Dcompss.monitor=${monitoring}
-Dcompss.tracing=${tracing}
-Dcompss.extrae.file=${custom_extrae_config_file}
-Dcompss.comm=${comm}
-Dcompss.conn=${conn}
-Dcompss.masterName=${master_name}
-Dcompss.masterPort=${master_port}
-Dcompss.scheduler=${scheduler}
-Dgat.adaptor.path=${GAT_LOCATION}/lib/adaptors
-Dgat.debug=false
-Dgat.broker.adaptor=sshtrilead
-Dgat.file.adaptor=sshtrilead
-Dcompss.worker.cp=${CLASSPATH}
-Dcompss.worker.jvm_opts=${jvm_workers_opts}
-Dcompss.worker.cpu_affinity=${worker_cpu_affinity}
-Dcompss.worker.gpu_affinity=${worker_gpu_affinity}
-Dcompss.profile.input=${input_profile}
-Dcompss.profile.output=${output_profile}
-Dcompss.scheduler.config=${scheduler_config}
-Dcompss.external.adaptation=${external_adaptation}
EOT
}

add_jvm_opts_java() {
  cat >> "${jvm_options_file}" << EOT
-noverify
-classpath ${CLASSPATH}:${COMPSS_HOME}/Runtime/compss-engine.jar
EOT
}

add_jvm_opts_c() {
  cat >> "${jvm_options_file}" << EOT
-Djava.class.path=${CLASSPATH}:${COMPSS_HOME}/Runtime/compss-engine.jar
-Dcompss.constraints.file=$fullAppPath.idl
-Dcompss.worker.persistent.c=${persistent_worker_c}
EOT
}

add_jvm_opts_python() {
  cat >> "${jvm_options_file}" << EOT
-Djava.class.path=${CLASSPATH}:${COMPSS_HOME}/Runtime/compss-engine.jar
-Dcompss.worker.pythonpath=${PYTHONPATH}
EOT
}

exec_java() {
  # Define command
  local java_opts
  local JAVACMD
  java_opts=$(cat "${jvm_options_file}" | tr '\n' ' ')
  JAVACMD=$JAVA" ${java_opts}"

  if [ $tracing -gt 0 ]; then
    export LD_PRELOAD=${EXTRAE_LIB}/libpttrace.so
  fi

  # Launch application
  $JAVACMD ${RUNTIME_LOADER} total "$fullAppPath" ${application_args}
  endCode=$?
  if [ $tracing -gt 0 ]; then
      unset LD_PRELOAD  # no tracing check because if not set does nothing
  fi
  if [ $endCode -ne 0 ]; then
    error_msg "${RUNTIME_ERROR}"
  fi
}

exec_c() {
  # Export needed variables
  if [ -d "${COMPSS_HOME}/Bindings/c" ]; then
    local CPP_COMPSS_HOME=${COMPSS_HOME}/Bindings/c
    export CPP_PATH=${CPP_COMPSS_HOME}:$cp
  else
    export CPP_PATH=$cp
  fi

  # Look for the JVM Library
  libjava=$(find "${JAVA_HOME}"/jre/lib/ -name libjvm.so | head -n 1)
  if [ -z "$libjava" ]; then
    libjava=$(find "${JAVA_HOME}"/jre/lib/ -name libjvm.dylib | head -n 1)
    if [ -z "$libjava" ]; then
      display_error "${JAVA_JRE_ERROR}"
    fi
  fi
  libjavafolder=$(dirname "$libjava")

  export LD_LIBRARY_PATH=$libjavafolder:${COMPSS_HOME}/Bindings/bindings-common/lib:${COMPSS_HOME}/Bindings/c/lib:${LD_LIBRARY_PATH}

  export JVM_OPTIONS_FILE=${jvm_options_file}

  # Launch application
  echo "JVM_OPTIONS_FILE: $JVM_OPTIONS_FILE"
  echo "COMPSS_HOME: $COMPSS_HOME"
  echo "Args: $application_args"
  echo " "

  if [ $tracing -gt 0 ]; then
    export LD_PRELOAD=${EXTRAE_LIB}/libpttrace.so
  fi
  $fullAppPath ${application_args}
  endCode=$?
  if [ $tracing -gt 0 ]; then
      unset LD_PRELOAD  # no tracing check because if not set does nothing
  fi
  if [ $endCode -ne 0 ]; then
    error_msg "${RUNTIME_ERROR}"
  fi
}

exec_python() {
  # Python Storage API
  storageApi=${COMPSS_HOME}/Dependencies/
  cp=$cp:$storageApi

  # Export needed variables
  if [ -d "${COMPSS_HOME}/Bindings/python" ]; then
    PYCOMPSS_HOME=${COMPSS_HOME}/Bindings/python
    export PYTHONPATH=${PYCOMPSS_HOME}:$PYTHONPATH
  fi

  # Look for the JVM Library
  libjava=$(find "${JAVA_HOME}"/jre/lib/ -name libjvm.so | head -n 1)
  if [ -z "$libjava" ]; then
    libjava=$(find "${JAVA_HOME}"/jre/lib/ -name libjvm.dylib | head -n 1)
    if [ -z "$libjava" ]; then
      display_error "${JAVA_JRE_ERROR}"
    fi
  fi     
  libjavafolder=$(dirname "$libjava")

  export LD_LIBRARY_PATH=$libjavafolder:${COMPSS_HOME}/Bindings/bindings-common/lib:${LD_LIBRARY_PATH}

  export JVM_OPTIONS_FILE=${jvm_options_file}

  # Launch application
  if [ $tracing -gt 0 ]; then
    export LD_PRELOAD=${EXTRAE_LIB}/libpttrace.so
  fi
  python -u "$PYCOMPSS_HOME"/pycompss/runtime/launch.py $log_level $PyObject_serialize $storageConf "$fullAppPath" ${application_args}
  endCode=$?
  if [ $tracing -gt 0 ]; then
      unset LD_PRELOAD  # no tracing check because if not set does nothing
  fi
  if [ $endCode -ne 0 ]; then
    error_msg "${RUNTIME_ERROR}"
  fi
}

clean_env() {
  # Delete JVM options file
  rm -f "${jvm_options_file}"

  # Delete tmp dir for initial loggers
  rm -rf "/tmp/$uuid"
}

#---------------------------------------------------
# MAIN EXECUTION
#---------------------------------------------------
  # Environment and arguments
  check_env
  get_args "$@"
  check_args

  # Trap to ensure clean end
  trap clean_env EXIT

  # Real runtime execution
  execute_runtime

