#!/usr/bin/env bash

# shellcheck shell=bash

################################################################################
######################### Main function (script init) ##########################
################################################################################

function __main__() {

  local _FUNCTION_ID="__main__"
  local _STATE="0"

  # Stores the current date.
  readonly _cdate=$(date +%Y%m%d)

  # Variables related to the log file. Divided into three parts due
  # to the better possibility of manipulation for the user.
  # shellcheck disable=SC2154
  readonly _log_directory="${_rel}/log"
  # shellcheck disable=SC2154
  readonly _log_file="${_init_name}.${_cdate}.log"
  readonly _log_stdout="${_log_directory}/stdout.log"
  readonly _log_path="${_log_directory}/${_log_file}"

  # We check if we are a root user.
  if [[ "$EUID" -ne 0 ]] ; then

    printf "EUID is not equal 0 (no root user)\\n"
    _exit_ "1"

  fi

  # Path to import file.
  # shellcheck disable=SC2154
  readonly _import_path="${_src}/import"

  # Path to modules directory.
  # shellcheck disable=SC2034,SC2154
  readonly _modules="${_dat}/modules"

  # Store modules list.
  # shellcheck disable=SC2034
  modules_list=()
  # shellcheck disable=SC2034
  modules_full_list=()
  # shellcheck disable=SC2034
  _module_commands_count=0
  # shellcheck disable=SC2034
  _module_commands_count_all=0

  # External configuration file (-c|--config script param).
  config=""
  load_state="0"

  # Declaration of output variables (--debug and --verbose params).
  stdout_mode=""
  printf_mode=""
  # Enable/disable output colors.
  # shellcheck disable=SC2034
  s_color="true"

  # Declaration of total execution time.
  time_tracking="false"
  time_mode="0"
  export _cmdtime_state="0"

  # We place here used commands at script runtime, as strings to anything
  # unnecessarily run.
  readonly commands=("basename" "dirname" "stat" "date" "grep" "egrep" "cut" \
                     "sed" "gzip" "tar" "nmap" "xterm" "proxychains")

  # If you intend to specify the full path to the command we do it like:
  # readonly exec_gzip="/bin/gzip"

  # Stores the names of the missing commands.
  missing_hash=()
  missing_counter="0"

  for i in "${commands[@]}" ; do

    if [[ ! -z "$i" ]] ; then

      hash "$i" >/dev/null 2>&1 ; state="$?"

      # If the command was not found put it in the array
      if [[ "$state" -ne 0 ]] ; then

        missing_hash+=("$i")
        ((missing_counter++))

      fi

    fi

  done

  # It is a good idea to terminate the script at this stage
  # with information for the user to fix the errors if at least one
  # of the required commands in the commands array is not found.
  if [[ "$missing_counter" -gt 0 ]] ; then

    printf "not found in PATH: %s\\n" "${missing_hash[*]}" >&2
    _exit_ "1"

  fi

  if [[ "$time_tracking" == "true" ]] ; then
    _begtime=$(date +%s) ; fi

  # shellcheck disable=SC2154
  _logger "init" \
    "init '${_init_name}' in '${_init_directory}'" && \
  _logger "info" \
    "__init_params[] = (${__init_params[*]})," \
    "__script_params[] = (${__script_params[*]})"

  # Include import file.
  _load "null" "$_import_path"

  # Specifies the call parameters of the script, the exact description
  # can be found in _help_ and file README.md.
  local _short_opt=""
  local _long_opt="help"

  _GETOPT_PARAMS=$(getopt -o "${_short_opt}" --long "${_long_opt}" \
                   -n "${_init_name}" -- "${__script_params[@]}")

  # With this structure, in the case of problems with the parameters placed
  # in the _GETOPT_PARAMS variable we finish the script. Keep this in mind
  # because it has some consequences - the __main __() function will not be
  # executed.

  # Ends an error if the parameter or its argument is not valid.
  _getopt_state="$?"
  if [ "$_getopt_state" != 0 ] ; then
    _exit_ "1"
  fi

  eval set -- "$_GETOPT_PARAMS"
  while true ; do

    case $1 in

      --help)

        _help_

        shift ; _exit_ "0" ;;

      *)

        if [[ "$2" == "-" ]] || [[ ! -z "$2" ]] ; then

          printf "%s: invalid option -- '%s'\\n" "$_init_name" "$2"
          _exit_ "1"

        # elif [[ -z "$2" ]] ; then break ; fi
        else break ; fi

        ;;

    esac

  done

  # If you run the script in debug mode, the information
  # will be displayed on the screen from this point.
  if [[ "$stdout_mode" == "debug" ]] ; then

    _logger "info" \
      "${_FUNCTION_ID}()" \
      "starting debug mode"

  fi

  # Running tasks before start user functions.
  _before_init

  ################################# USER SPACE #################################
  # ````````````````````````````````````````````````````````````````````````````
  # Put here all your variable declarations, function calls
  # and all the other code blocks.

  # In this section we add external file (for -c|--config script param).
  if [[ "$load_state" -eq 1 ]] ; then _load "head" "$config" ; fi

  # shellcheck disable=SC2034
  # Generate random value.
  _random=$(date +"%s")

  # Array that stores the names of variables used that are part of the script
  # call parameters (_GETOPT_PARAMS). Useful when checking whether all
  # or selected parameters without which the script can not work properly
  # have been used. Do not add the load_state variable to the _opt_values array,
  # which is supported above.
  _opt_values=()

  # Checking the value of the variables (if they are unset or empty):
  #   - variables for call parameters
  #   - variables from the additional configuration files
  if [[ "${#_opt_values[@]}" -ne 0 ]] ; then

    for i in "${_opt_values[@]}" ; do

      _i="" ; eval _i='$'"$i"

      _logger "info" \
        "${_FUNCTION_ID}()" \
        "$i: '$_i'"

      if [[ -z "$_i" ]] ; then

        _sprintf "stop" "error of argument value: '$i' is unset or empty"

        _logger "stop" \
          "${_FUNCTION_ID}()" \
          "error of argument value: '$i' is unset or empty"

      fi

    done

  fi

  # Only if you used --time, --verbose and --debug script parameters
  # - checking the relationship between call parameters.
  if [[ "$time_mode" -eq 1 ]] ; then

    if [[ "$printf_mode" != "verbose" ]] ; then

      _sprintf "stop" "missing argument: '--time' is dependent on '--verbose'"

      _logger "stop" \
        "${_FUNCTION_ID}()" \
        "missing argument: '--time' occurs only with '--verbose'"

    fi

  elif [[ "$stdout_mode" == "debug" ]] ; then

    if [[ "$printf_mode" == "verbose" ]] || [[ "$time_mode" -eq 1 ]] ; then

      _sprintf "stop" "debug mode can not occur together with '--verbose' and '--time'"

      _logger "stop" \
        "${_FUNCTION_ID}()" \
        "debug mode can not occur together with '--verbose' and '--time'"

    fi

  fi

  export _cmd_dest
  export _cmd_params
  export _cmd_report
  export _cmd_tor
  export _cmd_terminal

  # Initialize configuration.
  init_config

  # shellcheck disable=SC2154
  _cmd_dest="$dest"
  # shellcheck disable=SC2154
  _cmd_params="$params"
  # shellcheck disable=SC2154
  _cmd_report="$report"
  # shellcheck disable=SC2154
  _cmd_tor="$tor"
  # shellcheck disable=SC2154
  _cmd_terminal="$terminal"

  init_session

  # Initialize modules.
  load_modules

  # Initialize cli.
  clear
  init_cli

  # ````````````````````````````````````````````````````````````````````````````

  if [[ "$time_tracking" == "true" ]] ; then

    # Counting the execution time.
    _endtime=$(date +%s)
    _totaltime=$((_endtime - _begtime))

    # Print time header.
    printf '\e[m\e[1;39mTOTAL TIME: %dh:%dm:%ds\e[m\n' \
            $((_totaltime/3600)) $((_totaltime%3600/60)) $((_totaltime%60))

  fi

  return "$_STATE"

}
