#!/bin/bash

# Setting up COMPSs_HOME
if [ -z "${COMPSS_HOME}" ]; then
  COMPSS_HOME="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../../.. && pwd )/"
fi
if [ ! "${COMPSS_HOME: -1}" = "/" ]; then
  COMPSS_HOME="${COMPSS_HOME}/"
fi
export COMPSS_HOME=${COMPSS_HOME}

# Load auxiliar scripts

# shellcheck source=../system/commons/logger.sh"
# shellcheck disable=SC1091
source "${COMPSS_HOME}Runtime/scripts/system/commons/logger.sh"

# shellcheck source=../system/commons/utils.sh"
# shellcheck disable=SC1091
source "${COMPSS_HOME}Runtime/scripts/system/commons/utils.sh"

# shellcheck source=../system/commons/version.sh"
# shellcheck disable=SC1091
source "${COMPSS_HOME}Runtime/scripts/system/commons/version.sh"

# shellcheck source=../system/runtime/compss_setup.sh"
# shellcheck disable=SC1091
source "${COMPSS_HOME}Runtime/scripts/system/runtime/compss_setup.sh"

# shellcheck source=../system/agents/commons.sh"
# shellcheck disable=SC1091
source "${COMPSS_HOME}Runtime/scripts/system/agents/commons.sh"


###############################################
# SCRIPT CONSTANTS DECLARATION
###############################################
DEFAULT_DC_ENABLED="false"
DEFAULT_LOG_LEVEL_ARGUMENT=${LOG_LEVEL_DEBUG}

###############################################
# ERROR CONSTANTS DECLARATION
###############################################
TRACING_ERROR="ERROR: Value of option --tracing must be: false or true"


###############################################
###############################################
# Display functions
###############################################
###############################################

###############################################
# Display Usage
###############################################
usage() {
  cat << EOF
Usage: $0 [OPTION]...

COMPSs options:
EOF
  show_opts

  cat << EOF
General options:
    --help, -h                              Prints this message

    --version, v                            Prints COMPSs version
EOF
}

###############################################
# Display Arguments & Environment error
###############################################
arguments_error() {
  local error_msg=$1
  display_error "${error_msg}"
  usage
  exit 1
}

###############################################
# Display Runtime configuration
###############################################
display_settings () {
  display_info \
"Options setup:
    AGENT NAME: ${master_name}
    AGENT REST PORT: ${agent_rest_port}
    AGENT COMM PORT: ${agent_comm_port}

    RESOURCES FILE: ${resFile}
    PROJECT FILE: ${projFile}
    COMM ADAPTOR: ${comm}

    DEBUG: ${log_level}
    PYTHON INTERPRETER: ${python_interpreter}
    MPI PYTHON: ${python_mpi_worker}
    PYTHON CACHE: ${python_worker_cache}
    PYTHON PROFILER: ${python_cache_profiler}"
}


###############################################
###############################################
# Option management functions
###############################################
###############################################

###############################################
# Display Script Options
###############################################
show_opts() {

  cat << EOF
    --appdir=<path>                         Path for the application class folder.
                                          Default: ${DEFAULT_APPDIR}

    --classpath=<path>                      Path for the application classes / modules
                                          Default: Working Directory

    --comm=<className>                      Class that implements the adaptor for communications with other nodes
                                            Supported adaptors:
                                                ├── ${NIO_ADAPTOR}
                                                ├── ${GAT_ADAPTOR}
                                                ├── ${REST_AGENT_ADAPTOR}
                                                └── ${COMM_AGENT_ADAPTOR}
                                            Default: ${DEFAULT_COMMUNICATION_ADAPTOR}

    --comm_port=<int>                       Port on which the agent sets up a Comm interface. (<=0: Disabled)

    -d, --debug                             Enable debug. (Default: disabled)

    --hostname                              Name with which itself and other agents will identify the agent.

    --jvm_opts="string"                     Extra options for the COMPSs Runtime JVM. Each option separed by "," and without blank spaces (Notice the quotes)

    --library_path=<path>                   Non-standard directories to search for libraries (e.g. Java JVM library, Python library, C binding library)
                                            Default: Working Directory

    --log_dir=<path>                        Log directory. (Default: /tmp/${app_uuid})

    --log_level=<level>                     Set the debug level: ${LOG_LEVEL_OFF} | ${LOG_LEVEL_INFO} | ${LOG_LEVEL_API} | ${LOG_LEVEL_DEBUG} | ${LOG_LEVEL_TRACE}
                                            Default: ${DEFAULT_LOG_LEVEL}

    --master_port=<int>                     Port to run the COMPSs master communications.
                                            (Only when ${NIO_ADAPTOR} is used. The value is overriden by the comm_port value.)
                                            Default: ${DEFAULT_MASTER_PORT}

    --pythonpath=<path>                     Additional folders or paths to add to the PYTHONPATH
                                            Default: ${DEFAULT_PYTHONPATH}

    --python_interpreter=<string>           Python interpreter to use (python/python3).
                                            Default: $DEFAULT_PYTHON_INTERPRETER Version: ${DEFAULT_PYTHON_VERSION}

    --python_propagate_virtual_environment=<true>   Propagate the master virtual environment to the workers (true/false).
                                                    Default: $DEFAULT_PYTHON_PROPAGATE_VIRTUAL_ENVIRONMENT

    --python_mpi_worker=<false>             Use MPI to run the python worker instead of multiprocessing. (true/false).
                                            Default: $DEFAULT_PYTHON_MPI_WORKER

    --python_memory_profile                 Generate a memory profile of the master.
                                            Default: $DEFAULT_PYTHON_MEMORY_PROFILE

    --python_worker_cache=<string>          Python worker cache (true/size/false).
                                            Only for NIO without mpi worker and python >= 3.8.
                                            Default: ${DEFAULT_PYTHON_WORKER_CACHE}

    --python_cache_profiler=<bool>          Python cache profiler (true/false).
                                            Only for NIO without mpi worker and python >= 3.8.
                                            Default: ${DEFAULT_PYTHON_CACHE_PROFILER}

    --project=<path>                        Path of the project file
                                            (Default: ${DEFAULT_PROJECT})

    --resources=<path>                      Path of the resources file
                                            (Default: ${DEFAULT_RESOURCES})

    --rest_port=<int>                       Port on which the agent sets up a REST interface. (<=0: Disabled)

    --reuse_resources_on_block=<boolean>    Enables/Disables reusing the resources assigned to a task when its execution stalls.
                                            (Default:${DEFAULT_REUSE_RESOURCES_ON_BLOCK})

    --scheduler=<className>                 Class that implements the Scheduler for COMPSs
                                            Supported schedulers:
                                                  ├── ${BASE_SCHEDULER}
                                                  ├── ${OS_FIFO_SCHEDULER}
                                                  ├── ${LA_FIFO_SCHEDULER}
                                                  ├── ${LA_LIFO_SCHEDULER}
                                                  ├── ${LA_LOCALITY_SCHEDULER}
                                                  ├── ${LA_SUCC_CONSTRAINTS_FIFO_SCHEDULER}
                                                  ├── ${LA_MT_SUCC_CONSTRAINTS_FIFO_SCHEDULER}
                                                  ├── ${LA_SUCC_FIFO_SCHEDULER}
                                                  ├── ${LA_MT_SUCC_FIFO_SCHEDULER}
                                                  ├── ${LA_SUCC_LIFO_SCHEDULER}
                                                  ├── ${LA_MT_SUCC_LIFO_SCHEDULER}
                                                  ├── ${LA_SUCC_LOCALITY_SCHEDULER}
                                                  └── ${LA_MT_SUCC_LOCALITY_SCHEDULER}
                                            Default: ${DEFAULT_SCHEDULER}

    --scheduler_config_file=<path>          Path to the file which contains the scheduler configuration.
                                            Default: Empty

    --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

    --summary                               Displays a task execution summary at the end of the application execution
                                            Default: ${DEFAULT_SUMMARY}

    --tracing=<bool>, --tracing, -t         Set generation of traces.
                                            Default: ${DEFAULT_TRACING}

    --trace_label=<string>                  Add a label in the generated trace file. Only used in the case of tracing is activated.
                                            Default: None

    --wdir=<path>                           Directory used by the agent to store the temporary files

EOF
}

###############################################
# Parses the options from the commandline and updates the current option values
###############################################
parse_options() {
  while getopts hvgtmdp-: flag; do
    # Treat the argument
    case "${flag}" in
      d)
        # Enable debug in log level
        log_level=${DEFAULT_LOG_LEVEL_ARGUMENT}
        ;;
      h)
        # Display help
        usage
        exit 0
        ;;
      t)
        # Enable tracing
        tracing="${TRACING_ENABLED}"
        ;;
      v)
        # Display version
        show_version
        exit 0
        ;;
      -)
	      # Check more complex arguments
	      case "$OPTARG" in
        # Options Description options
          opts)
            # Show launch options
            show_opts
            exit 0
            ;;
        	help)
	          # Display help
	          usage
            exit 0
	          ;;

          # Version options
          flower)
            # Display flower
            show_flower
            exit 0
            ;;
          recipe)
            # Display recipe
            show_recipe
            exit 0
            ;;
          version)
            # Show version
            show_full_version
            exit 0
            ;;

          # COMPSs options
          appdir=*)
	          # Main binary directory (only for C/C++ applications)
	          appdir=${OPTARG//appdir=/}
	          ;;
          hostname=*)
            master_name=${OPTARG//hostname=/}
            ;;
	        classpath=*)
	          # Additional classpath
	          cp=${OPTARG//classpath=/}
	          ;;
          comm=*)
            comm=${OPTARG//comm=/}
            ;;
          comm_port=*)
            agent_comm_port=${OPTARG//comm_port=/}
            export COMM_AGENT_PORT=${agent_comm_port};
            ;;
          coverage=*)
            jacoco_agent_expression=${OPTARG//coverage=/}
            coverage=true
            ;;
          custom_threads=*)
            # Specify if the tracing system re-organizes the threads
            tracing_custom_threads=${OPTARG//custom_threads=/}
            ;;
          debug)
            # Enable debug in log level
            log_level=${DEFAULT_LOG_LEVEL_ARGUMENT}
            ;;
          generate_trace=*)
            # Specify if the tracing system merges the events in a trace file
            tracing_generate_trace=${OPTARG//generate_trace=/}
            ;;
          input_profile=*)
            # Specify the file where there is stored the tasks profiles
            input_profile=${OPTARG//input_profile=/}
            ;;
          jvm_opts=*)
            # Other java flags
            jvm_master_opts=${OPTARG//jvm_opts=/}
            ;;
	        library_path=*)
	          # Additional library path
	          library_path=${OPTARG//library_path=/}
	          ;;
          log_dir=*)
            # Set a custom specific log dir
            specific_log_dir=${OPTARG//log_dir=/}
            ;;
          log_level=*)
            # Enable different log_levels by user selection
            log_level=${OPTARG//log_level=/}
            ;;
          master_port=*)
            # Preset a Master port
            master_port=${OPTARG//master_port=/}
            ;;
          output_profile=*)
            # Specify the file where COMPSs will store the tasks profile
            output_profile=${OPTARG//output_profile=/}
            ;;
          pythonpath=*)
            # Additional pythonpath
            pythonpath=${OPTARG//pythonpath=/}
            ;;
          python_interpreter=*)
            # Specify the python interpreter to use
            python_interpreter=${OPTARG//python_interpreter=/}
            ;;
          python_propagate_virtual_environment=*)
            # Enable or disable the virtual environment propagation
            python_propagate_virtual_environment=${OPTARG//python_propagate_virtual_environment=/}
            ;;
          python_mpi_worker=*)
            # Enable or disable the virtual environment propagation
            python_mpi_worker=${OPTARG//python_mpi_worker=/}
            ;;
          python_memory_profile)
            # Enable or disable the master memory profiling
            python_memory_profile=true
            ;;
          python_worker_cache=*)
            # Enable or disable the python worker cache
            python_worker_cache=${OPTARG//python_worker_cache=/}
            ;;
          python_cache_profiler=*)
            # Enable or disable the python worker cache
            python_cache_profiler=${OPTARG//python_cache_profiler=/}
            ;;
          project=*)
	          # Custom project file
	          projFile=${OPTARG//project=/}
	          if [ ! -f "$projFile" ]; then
	            display_warning "Project XML file '$projFile' could not be found."
	          fi
	          ;;
 	        resources=*)
	          # Custom resources file
	          resFile=${OPTARG//resources=/}
	          if [ ! -f "$resFile" ]; then
	            display_warning "Resources XML file '$resFile' could not be found."
	          fi
            ;;
          rest_port=*)
            agent_rest_port=${OPTARG//rest_port=/}
            export REST_AGENT_PORT=${agent_rest_port};
            ;;
          reuse_resources_on_block=*)
            # Enable or disable the reusing resources
            reuse_resources_on_block=${OPTARG//reuse_resources_on_block=/}
            ;;
          scheduler=*)
            # Scheduler main class
            scheduler=${OPTARG//scheduler=/}
            ;;
          scheduler_config_file=*)
            # Specify the file where COMPSs will store the tasks profile
            scheduler_config=${OPTARG//scheduler_config_file=/}
            ;;
          summary)
            summary=true
            ;;
          tracing=*)
            # Tracing system
            tracing=${OPTARG//tracing=/}
              # Tracing level
              if [ "${tracing}" == "false" ]; then
                tracing=${TRACING_DEACTIVATED}
              elif [ "${tracing}" == "true" ]; then
                tracing=${TRACING_ENABLED}
              else
                arguments_error "${TRACING_ERROR}"
              fi
            ;;
          tracing)
            # Tracing system
            tracing=${TRACING_ENABLED}
            ;;
          trace_label=*)
            # Tracing system
            trace_label=${OPTARG//trace_label=/}
            ;;
          wdir=*)
            # working directory for master
            wdir_in_master=${OPTARG//wdir=/}
            ;;
        *)
	        # Flag didn't match any patern. Raise exception
	        arguments_error "Bad argument: $OPTARG"
	        ;;
        esac
        ;;
      *)
	      # Flag didn't match any patern. Raise exception
          arguments_error "Bad argument: $flag"
	      ;;
    esac
  done

}

###############################################
# Validates the current configuration
###############################################
check_setup() {
  if [ -z "${master_name}" ]; then
    arguments_error "ERROR! HOSTNAME not set"
  fi

  if  [[ "${comm}" == "${COMM_AGENT_ADAPTOR}" ]]; then
    if [[ -z "${agent_comm_port}" ]]; then
	      fatal_error "When using the Comm Agent adaptor, COMM port needs to be specified (--comm_port)" 1
    fi
  fi

  if  [[ "${comm}" == "${REST_AGENT_ADAPTOR}" ]]; then
    if [[ -z "${agent_rest_port}" ]]; then
	      fatal_error "When using the REST Agent adaptor, REST port needs to be specified (--rest_port)" 1
    fi
  fi

  if  [[ "${comm}" == "${NIO_ADAPTOR}" ]]; then
    if [[ -z "${agent_rest_port}" ]]; then
	      master_port=${agent_rest_port}
    fi
  fi

  if [ -n "${agent_comm_port}" ]; then
    CLASSPATH="${CLASSPATH}:${COMPSS_HOME}Runtime/adaptors/CommAgent/worker/compss-adaptors-agent-comm-worker.jar"
    comm_port_msg="(comm: ${agent_comm_port})"
  fi
  if [ -n "${agent_rest_port}" ]; then
    CLASSPATH="${CLASSPATH}:${COMPSS_HOME}Runtime/adaptors/RESTAgent/worker/compss-adaptors-agent-rest-worker.jar"
    rest_port_msg="(rest: ${agent_rest_port})"
  fi

  get_uuid
  if [ -z "${uuid}" ]; then
    fatal_error "UUID is empty" 1
  fi

  if [ -z "${specific_log_dir}" ]; then
    specific_log_dir="/tmp/${uuid}"
  fi

  if [ -z "${enabled_nested_local}" ]; then
    enabled_nested_local="true"
  fi

  WORKING_DIR="${specific_log_dir}"
  rm -rf "${WORKING_DIR}"
  mkdir -p "${WORKING_DIR}"
  CONFIG_DIR="${WORKING_DIR}/cfgfiles/"
  mkdir -p "${CONFIG_DIR}"

  check_compss_setup
}


###############################################
###############################################
# Secondary functions
###############################################
###############################################

clean_script(){
  if [ -n "${WORKING_DIR}" ]; then
    rm -rf "${WORKING_DIR}"
  fi
}

clean_agent(){
    clean_runtime_environment
}


###############################################
###############################################
# Main code
###############################################
###############################################
check_compss_env


parse_options "$@"
# trap clean_script EXIT
check_setup
display_settings

# trap clean_agent EXIT

display_info \
"-----------------------------------------------
              AGENT  ${master_name}
-----------------------------------------------
Launching COMPSs agent on device ${master_name} and ports ${rest_port_msg} ${comm_port_msg} with debug level ${DEBUG}"

start_compss_agent
