#!/usr/bin/env bash

# docker-finance | modern accounting for the power-user
#
# Copyright (C) 2025 Aaron Fiore (Founder, Evergreen Crypto LLC)
#
# 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 <https://www.gnu.org/licenses/>.

#
# "Libraries"
#

[ -z "$DOCKER_FINANCE_CONTAINER_REPO" ] && exit 1
source "${DOCKER_FINANCE_CONTAINER_REPO}/src/finance/lib/internal/lib_utils.bash" || exit 1

#
# Facade
#

function lib_root::root()
{
  lib_root::__parse_root "$@"
  lib_root::__root "$@"
  lib_utils::catch $?
}

#
# Implementation
#

function lib_root::__parse_root()
{
  [ -z "$global_usage" ] && lib_utils::die_fatal

  local -r _arg="$1"
  local -r _usage="
\e[32mDescription:\e[0m

  docker-finance ROOT.cern interpreter

\e[32mUsage:\e[0m

  $ $global_usage [help | [TAB COMPLETION]] [args]

\e[32mArguments:\e[0m

  None: start interactive interpreter w/out running any given macro or plugins

  [TAB COMPLETION]: load (and run) given macro/plugin when starting interpreter

    macro           = repository macros in repository location
    plugins/custom  = custom plugins in custom plugin location
    plugins/repo    = repository plugins in repository location

  [args]: arguments to macro or plugin

\e[32mExamples:\e[0m

  \e[37;2m# See this usage help\e[0m
  $ $global_usage help

  \e[37;2m# The output of tab completion\e[0m
  $ $global_usage \\\t\\\t
    help                       macro/crypto/random.C      macro/test/unit.C          plugins/custom/example.cc
    macro/crypto/hash.C        macro/test/benchmark.C     macro/web/server.C         plugins/repo/example.cc

  \e[37;2m# Load and run the repo's Web server macro\e[0m
  \e[37;2m# NOTE: this macro will maintain a running ROOT instance\e[0m
  $ $global_usage macro/web/server.C

  \e[37;2m# Load and run the repo's Hash macro with an argument\e[0m
  \e[37;2m# NOTE: this macro will 'one-off' the ROOT instance\e[0m
  $ $global_usage macro/crypto/hash.C 'this is a test message'

  \e[37;2m# Load repo example plugin, run within instance\e[0m
  $ $global_usage plugins/repo/example.cc
  root [0]
  Interpreting: '.L /home/business/docker-finance/plugins/root/example.cc'
  root [1] dfi::plugin::my_plugin_namespace::example\\\t
  example1
  example2
  example3
  root [1] dfi::plugin::my_plugin_namespace::example2()
  ...
"

  if [[ "$#" -gt 0 && ! "$_arg" =~ (^macro/|^plugins/) ]]; then
    lib_utils::die_usage "$_usage"
  fi

  # Type determined by path stub
  local -r _stub="${_arg#*/}"

  #
  # Macro
  #

  if [[ "$_arg" =~ ^macro ]]; then
    # TODO: utilize stub when 'repo/' and 'custom/' is implemented (like plugins)
    local -r _macro="$_arg"

    # Repo macros
    [ ! -f "${DOCKER_FINANCE_CONTAINER_REPO}/src/root/${_macro}" ] \
      && lib_utils::die_fatal "repo macro not found '${_macro}'"

    # TODO: currently, only repo macros are supported
    #   (no custom macros, use custom plugins instead)

    # Per API (v1), requires path stub format
    declare -gr global_arg_macro="$_macro"
  fi

  #
  # Plugin
  #

  if [[ "$_arg" =~ ^plugins ]]; then
    local -r _plugin="${_stub#*/}"

    # Repo plugins
    if [[ "$_stub" =~ ^repo ]]; then
      [ ! -f "${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/${_plugin}" ] \
        && lib_utils::die_fatal "repo plugin not found '${_plugin}'"
    fi

    # Custom plugins
    if [[ "$_stub" =~ ^custom ]]; then
      [ -z "$DOCKER_FINANCE_CONTAINER_PLUGINS" ] && lib_utils::die_fatal
      [ ! -f "${DOCKER_FINANCE_CONTAINER_PLUGINS}/root/${_plugin}" ] \
        && lib_utils::die_fatal "custom plugin not found '${_plugin}'"
    fi

    # Per API (v1), requires path stub format
    declare -gr global_arg_plugin="$_stub"
  fi
}

function lib_root::__root()
{
  lib_utils::deps_check "root"

  local -r _path="${DOCKER_FINANCE_CONTAINER_REPO}/src/root"
  pushd "$_path" 1>/dev/null || lib_utils::die_fatal "could not pushd '${_path}'"

  #
  # Base command
  #

  # NOTE:
  #
  # rootlogon.C *MUST* be loaded at any startup; as if fulfills loading
  # of `dfi` source files and other library requirements.
  #
  # Typically, ROOT.cern would load rootlogon.C if we start `root`
  # interactively within the macro directory. However, `dfi` has its own
  # requirements.
  #
  # After rootlogon, any loading of macros, plugins or any `dfi` files
  # can be completed using the `dfi` API.

  local _exec=("root" "-l" "macro/rootlogon.C")

  #
  # Interactive opts
  #

  # Start interactive instance w/out loading/running macro or plugin
  if [[ -z "$global_arg_macro" && -z "$global_arg_plugin" ]]; then
    _exec+=("-e" "help()")
  fi

  #
  # Macro: non-interactive opts
  #

  if [ ! -z "$global_arg_macro" ]; then
    _exec+=("-e" "dfi::macro::load(\"${global_arg_macro}\")")

    # NOTE:
    #
    # - Currently, only repo macros are supported
    #
    #   * Per API (v1):
    #
    #     - filename is named after class
    #       * classes begin uppercase
    #       * filename begins lowercase
    #
    #     - run() static function is the entry point for all macros
    #

    # TODO: modify substring when 'repo/' and 'custom/' is implemented (like plugins)

    local _namespace="${global_arg_macro#*/}"
    _namespace="${_namespace%/*}"
    declare -r _namespace

    local _class="${global_arg_macro##*/}"
    _class="${_class^}"
    _class="${_class%.*}"
    declare -r _class

    local -r _caller="dfi::macro::${_namespace}::${_class}"

    # Per-API signatures (v1)
    if [[ ${_class} == "Hash" ]]; then
      # If no message is given, default to unique profile/subprofile
      [[ -z "$global_parent_profile" || -z "$global_arg_delim_1" || -z "$global_child_profile" ]] && lib_utils::die_fatal
      [ -z "${*:2}" ] \
        && _run="run(\"${global_parent_profile}${global_arg_delim_1}${global_child_profile}\")" \
        || _run="run(\"${*:2}\")"
    else
      _run="run()"
    fi

    _exec+=("-e" "${_caller}::${_run}")

    # NOTE: when called via shell, server(s) are implied to
    #   require an interactive ROOT instance (not a 'one-off').
    if [[ "${_class%.*}" != "Server" ]]; then
      _exec+=('-q')
    fi
  fi

  #
  # Plugin: non-interactive opts
  #

  if [ ! -z "$global_arg_plugin" ]; then
    _exec+=("-e" "dfi::plugin::load(\"${global_arg_plugin}\")")
  fi

  #
  # Execute
  #

  lib_utils::print_debug "${_exec[@]}"
  "${_exec[@]}" || lib_utils::die_fatal "root exited with: $?"
}

# vim: sw=2 sts=2 si ai et
