#!/usr/bin/env bash

# ## package\_error
#
# Presents the user with a package related error message including the tail
# of an optional log file.
#
# ### Input Parameters
#
# String to be logged, an optional log file containing more details and
# an optional number of lines of the file to show (defaults to 25)
#
# ### Stream Outputs
#
# Prints arguments passed in to the calling environments STDOUT with a newline
# character appended and 'ERROR ' prepended.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 1 for failure.
#
# ### Failure Scenarios
#
# Fails if no arguments are given.
#
# ### Usage Examples
#
#     user$ package_error "Hello there! " configure.log 5
#     ERROR Hello there!
#
#
#     Tail of configure.log:
#			checking for inflate in -lz... no
#			configure: error: zlib library not found
#			If you have zlib already installed, see config.log for details on the
#			failure.  It is possible the compiler isn't looking in the proper directory.
#			Use --without-zlib to disable zlib support.
#
#     *poof* shell closed...
#
package_error()
{
  local _message=$1
  if [[ -z "${_message}" ]]
  then
    fail "Cannot print a package error as no message was provided."
  fi
  local _log=$2

  if [[ -n ${_log} ]] && file_exists ${_log}
  then # Append tail of error log to the error message.
    local _num_lines=${3:-25} # Default the number of lines to 25
    _message="${_message}\n\nTail of ${_log}:\n$( tail -n ${_num_lines} ${_log} )"
  fi

  error "${_message}"
}

#
# ## package\_definition
#
# Enables module developers to easily set package variables in name, value pairs.
#
# ### Input Parameters
#
# Parameters must come in pairs, variable name first (without the 'package'
# prefix), value second.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# For each name/value pair, a variable package_${name} is created with the given
# value.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Fails if extension developer accidentailly separates key/value with an = :)
# Fails if a key is given without a matching value.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_definition \
#         name "bash" \
#         version "4.2" \
#         url "ftp.gnu.org/gnu/bash/"
#
# ### Notes
#
# For readability it is recommended to split the line into "key value" lines by
# ending the line with a singele backslash '\' character with no space afterwards.
#
package_definition()
{
  while (( $# > 0 )) ; do
    key="$1" ; shift
    if [[ -n "${1:-}" ]] ; then
      value="$1" ; shift
    else
      if echo "$key" | grep '=' ; then
        fail "Invalid key '${key}'\n => Guess: Most likely the '=' is supposed to be a ' '."
      else
        fail "Value not specified for key '${key}'\n => They tend to come in pairs :)"
      fi
    fi

    case "$key" in
      name)
        package_name="${value}"
        ;;
      version)
        package_version="${value}"
        ;;
      file)
        package_file="${value}"
        ;;
      dir)
        package_dir="${value}"
        ;;
      url)
        package_url="${value}"
        ;;
      base_url)
        package_base_url="${value}"
        ;;
      docs_url)
        package_docs_url="${value}"
        ;;
      patches_url)
        package_patches_url="${value}"
        ;;
      md5_url)
        package_md5_url="${value}"
        ;;
      active_path)
        active_path="${value}"
        ;;
      bin_path)
        bin_path="${value}"
        ;;
      packages_path)
        packages_path="${value}"
        ;;
      source_path)
        source_path="${value}"
        ;;
      target_path)
        target_path="${value}"
        ;;
      archive_format)
        archive_format="${value}"
        ;;
      # TODO: Simplify into
      #(+([[[:alnum:]]|])_path|name|version|url|md5_url)
      #  eval "${key}=${value}"
      #  ;;
      *)
        fail "Unknown key '${key}' (value: ${value})"
      ;;
    esac
  done
}

#
# ## package\_install
#
# Performs all package installation steps:
# * download
# * extract
# * patch
# * confiure
# * build
# * install
# * postinstall
# * activation
# * service setup, if service module is loaded
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# Steps performed are logged to STDOUT of the calling environment.
#
# ### Environmental effects
#
# Package installation artifacts are created in the system and source directories.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Fails if any of the constituant components fail.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_definition \
#         name "bash" \
#         version "4.2" \
#         url "ftp.gnu.org/gnu/bash/"
#
# user$ package_install
#
# ### Notes
#
package_install()
{
  variables_must_be_nonempty package_name package_version archive_format

  if symlink_exists "${packages_path}/${package_name}/active" \
  && [[ "$1" != "--force" ]]
  then
    log "
Package ${package_name} ${package_version} already installed
To force call:

  bdsm ${package_name} package install --force
"
    exit 0
  fi

  true "${package_dir:="${package_name}-${package_version}"}"

  ensure_paths_exist "${source_path}"

  enter "${source_path}"

  if command_exists "${package_name}_dependencies"
  then
    "${package_name}_dependencies"
  else
    log "Installing dependencies for ${package_name} ${package_version}"
    package_dependencies
  fi

  if command_exists "${package_name}_prefetch"
  then
    "${package_name}_prefetch"
  fi

  if command_exists "${package_name}_fetch"
  then
    "${package_name}_fetch"
  else
    #log "Fetching ${package_name} ${package_version}"
    package_fetch
  fi

  enter "${source_path}/${package_dir}"

  if command_exists "${package_name}_postfetch"
  then
    "${package_name}_postfetch"
  fi

  if command_exists "${package_name}_patch"
  then
    "${package_name}_patch"
  else
    package_patch
  fi

  if command_exists "${package_name}_preconfigure"
  then
    "${package_name}_preconfigure"
  fi

  if command_exists "${package_name}_configure"
  then
    "${package_name}_configure"
  else
    log "Configuring ${package_name} ${package_version}"
    package_configure
  fi

  if command_exists "${package_name}_postconfigure"
  then
    "${package_name}_postconfigure"
  fi

  if command_exists "${package_name}_build"
  then
    "${package_name}_build"
  else
    log "Building ${package_name} ${package_version}"
    package_build
  fi

  if command_exists "${package_name}_preinstall"
  then
    "${package_name}_preinstall"
  else
    package_preinstall
  fi

  if command_exists "${package_name}_install"
  then
    "${package_name}_install"
  else
    log "Installing ${package_name} ${package_version}"
    package_make_install
  fi

  if command_exists "${package_name}_postinstall"
  then
    "${package_name}_postinstall"
  fi

  package_activate_if_first "${package_name}" "${package_version}"

  if command_exists "${package_name}_postactivate"
  then
    "${package_name}_postactivate"
  fi

  package_setup
}

#
# ## package\_update
#
package_update()
{
  # TODO: check if newer version exists, if so then...
  package_install
  package_activate ${package_name} ${package_version}
}

#
# ## package\_fetch\_md5
#
# Fetches the package's md5 sum from the md5_url, if given.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# The 'package_md5' variable is set with the downloaded md5 sum.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Fails if any of the constituant components fail.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_fetch_md5
#
# ### Notes
#
package_fetch_md5()
{
  variables_must_be_nonempty package_name package_version archive_format

  local download_url

  : \
    "${package_file:="${package_name}-${package_version}.${archive_format}"}" \
    "${packge_md5_url:="${package_base_url}/${package_file}.md5"}" \
    "${package_md5:=$( hash_file "${extension_config_path}/md5" "${package_file}" )}"

  if [[ -z "${package_md5}" && -n "${package_md5_url}" ]]
  then
    curl -L "${packge_md5_url}" -o "${archives_path}/${package_file}.md5" 2>/dev/null ||
      error "Fetching md5 from '${package_md5_url}' failed."

    package_md5=$(cat "${archives_path}/${package_file}.md5")

    rm "${archives_path}/${package_file}.md5"
  fi
}

#
# ## package\_fetch
#
# Fetches the package and extract it to source.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# scm output goes to STDERR of the calling environment.
#
# ### Environmental effects
#
# The archive file will be fetched to filesystem.
# The archive file will be extracted to filesystem.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_fetch
#
# ### Notes
#
package_fetch()
{
  variables_must_be_nonempty package_name package_version archive_format

  : \
    "${package_file:="${package_name}-${package_version}.${archive_format}"}" \
    "${package_url:="${package_base_url}/${package_file}"}"

  package_fetch_md5

  fetch_uri "${package_url}" "${source_path}/${package_dir}" "${package_md5:-#}"

  if [[ -d "${source_path}/${package_dir}/${package_dir}" ]]
  then
    eval mv "${source_path}/${package_dir}/${package_dir}/*" "${source_path}/${package_dir}/"
    rm -rf "${source_path}/${package_dir}/${package_dir}/"
  fi
}

#
# ## package\_configure
#
# Configures the package source (eg. ./configure ...).
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Makefile will be generated for a standard package extension.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if configuration fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_configure
#
# ### Notes
#
package_configure()
{
  local _command

  if variable_is_nonempty configure_command
  then
    _command="${configure_command}"
  else
    export PREFIX="${install_base_path}/${package_version}"

    _command="./configure ${configure_flags[@]:-"--prefix=${install_path}"}"

    file_is_executable "configure" || return 0
  fi

  debug package "package_configure: ${_command}"
  if ${_command} > configure.log 2>&1
  then
    return 0
  else
    package_error "Configuration of ${package_name} ${package_version} failed" "configure.log"
  fi
}

#
# ## package\_build
#
# builds the package source (eg. make)
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Makefile will be generated for a standard package extension.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if building fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_build
#
# ### Notes
#
package_build()
{
  local _command

  if [[ -n ${make_command:-} ]]
  then
    _command="${make_command}"
  else
    _command="make ${make_flags[@]:-"-j$(os_cpu_count)"}"
  fi

  debug package "package_build: ${_command}"
  if eval "${_command}" > make.log 2>&1
  then
    return 0
  else
    package_error "Compilation of ${package_name} ${package_version} failed! " "make.log"
  fi
}

#
# ## package\_preinstall
#
# Link compilation dir to installation dir/src
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Compilation dir will be available as src in installation directory
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# None
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_preinstall
#
# ### Notes
#
package_preinstall()
{
  link --force "${source_path}/${package_dir}" to "${install_path}/src"
}

#
# ## package\_make\_install
#
# make install  the package source (eg. make)
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Installation files will be installed to the install_path.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if make install fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_make_install
#
# ### Notes
#
package_make_install()
{
  local _command

  if [[ -n ${make_install_command:-} ]]
  then
    _command="${make_install_command}"
  else
    _command="make ${make_install_flags[@]:-install}"
  fi

  if ${_command} > make.install.log 2>&1
  then
    return 0
  else
    package_error "Installation of ${package_name} ${package_version} failed! " "make.install.log"
  fi
}

#
# ## package\_activate
#
# activates the package
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Symlink will be created to the active version in the package install path.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if activateing fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_activate
#
# ### Notes
#
package_activate()
{
  # TODO: Switch package activation to
  local _package="${1:-}" _version="${2:-}" _file _files=() _path _paths

  variables_must_be_nonempty _package _version

  true "${install_path:="${packages_path}/${_package}/${_version}"}"

  if path_exists "${install_path}"
  then
    if symlink_exists "${packages_path}/${_package}/active"
    then
      package_deactivate "${_package}"
    fi

    log "Activating ${_package} ${_version}"

    _paths=($( find "${install_path}" -type d ))
    for _path in "${_paths[@]}"
    do
      ensure_paths_exist "${active_path}${_path##${install_path}}"
    done

    _files=($( find "${install_path}" -type f ))
    for _file in "${_files[@]}"
    do
      link --force "${_file}" \
        to "${active_path}${_file##${install_path}}"
    done

    link --force "${install_path}" \
      to "${packages_path}/${_package}/active"
  else
    warn "Skipping activation of ${_package} ${_version} "\
      "as '${install_path}' does not exist."
  fi

  package_setup "${_package}"
}

package_activate_action()
{
  if [[ -z "${package_version}" ]]
  then
    error "You need to specify version to activate:\n  bdsm ${package_name}:<version> activate\n  bdsm ${package_name} activate --version <version>"
  fi
  package_activate ${package_name} ${package_version}
}

#
# ## package\_setup
#
# Sets up the package
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# ldconfig, profile.d and service (init.d / conf.d) files will be put in place
# on the system.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if setup fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_setup
#
# ### Notes
#
package_setup()
{
  local _package="${1:-${package_name}}"

  package_ldconfig "${_package}"

  package_profile_d "${_package}"

  if module_is_loaded "service"
  then
    service_setup "${_package}"
  fi

  if module_is_loaded "database"
  then
    database_setup "${_package}"
  fi
}

#
# ## package\_deactivate
#
# deactivates the package
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Active symlinks will be removed from the filesystem.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if deactivating fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_deactivate
#
# ### Notes
#
package_deactivate()
{
  local _package="${1:-}" _file _files _version

  variables_must_be_nonempty _package

  log "Deactivating ${_package}"

  if symlink_exists "${packages_path}/${_package}/active"
  then
    _version=$(readlink "${packages_path}/${_package}/active")
    _version=${_version##*/}
  else
    return 0
  fi

  true "${install_path:="${packages_path}/${_package}/${_version}"}"

  _files=($( find "${install_path}" -type f ))
  for _file in "${_files[@]}"
  do
    remove_files --force "${_file}"
  done

  _files=(
  "/etc/ld.so.profile.d/${_package}.conf"
  "/etc/profile.d/${_package}.sh"
  "${packages_path}/${_package}/active"
  )

  for _file in "${_files[@]}"
  do
    if file_exists "${_file}"
    then
      remove_files "${_file}"
    fi
  done

  log "${_package} deactivated"
}

#
# ## package\_activate\_if\_first
#
# Activates the package version , if another package version has not yet been
# activated.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Active symlinks will be added to the filesystem if it is the first version
# installed.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if deactivating fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_activate_if_first
#
# ### Notes
#
package_activate_if_first()
{
  local _package="${1:-}" _version="${2:-}"

  variables_must_be_nonempty _package _version

  if path_exists "${install_path}"
  then
    symlink_exists "${packages_path}/${_package}/active" ||
      package_activate "${_package}" "${_version}"
  else
    log "'${install_path}' not found, skipping activation."
  fi
}

#
# ## package\_ldconfig
#
# Sets up system level ldconfigs for the package libraries.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Adjusts ldconfig configuration and runs ldconfig (on linux).
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if deactivating fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_ldconfig
#
# ### Notes
#
package_ldconfig()
{
  local _package="${1:-${package_name}}" _path _files

  variables_must_be_nonempty _package

  path_exists "${packages_path}/${_package}/active/lib" ||
    return 0 # no lib/ directory for activated package, no need to update ldconfig

  # TODO: Figure out if solaris and freebsd have an analog to this?
  if user_is_root
  then
    if os_is_linux
    then
      ensure_paths_exist "/etc/ld.so.conf.d"

      if ! file_exists "/etc/ld.so.conf.d/bdsm.conf"
      then
        printf "%s\n" "${active_path}/lib" > "/etc/ld.so.conf.d/bdsm.conf"
        chmod_files 0644 "/etc/ld.so.conf.d/bdsm.conf"
      fi

      log "Updating ldconfig for ${_package}"
      ldconfig
    elif os_is_darwin
    then # Cluster Fuck.
      true
      # This should be handled by the profile.d?
      # _files=($(find "${packages_path}/${_package}/active" -mindepth 1 -maxdepth 1 -type d))
      # (( ${#_files[@]} > 0 )) && copy_files to "${packages_path}" "${_files[@]}"
    fi
  fi
}

#
# ## package\_profile\_d
#
# Sets up system level profile_d configuration for the package
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Updates profile_d configuration for the given package
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if profile_d setup fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_profile_d
#
# ### Notes
#
package_profile_d()
{
  local _package="${1:-${package_name}}"

  variables_must_be_nonempty _package

  if template_exists "profile.d.template"
  then
    log "Updating shell profile for ${_package}"

    ensure_paths_exist "${profile_d_path}"
    ensure_files_exist "${profile_path}"

    install_template "profile.d" to "${profile_d_path}/${_package}.sh" mode 0755
  fi
}

#
# ## package\_uninstall
#
# Uninstalls the package
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Uninstalls the package install effects from the system.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if uninstall fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_uninstall
#
# ### Notes
#
package_uninstall()
{
  variables_must_be_nonempty package_name package_version archive_format

  if package_is_active "${package_name}" "${package_version}"
  then # Deactivate the package if active.
     package_deactivate "${package_name}"
  fi

  remove_paths "${install_path}"

  if module_is_loaded service
  then # Remove any service reminants.
     service_uninstall
  fi

  log "${package_name} ${package_version} has been uninstalled."
}

#
# ## package\_patch
#
# Applies any patches found for the current package.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Updates the source code directory for the package with any patches found.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if patching fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_patch
#
# ### Notes
#
package_patch()
{
  local _patches _patch

  if ! path_exists "${extension_patches_path}"
  then
    return 0
  fi

  # TODO: Three level hierarchy based on patches/{OS}/{version},
  #       root level as 'global' always installed.

  # TODO: Test each dir for existence and skip if missing
  _patches=($(find "${extension_patches_path}" -mindepth 1 -maxdepth 1 -iname '*.patch' -type f))

  if (( ${#_patches[@]} == 0 ))
  then
    return 0
  fi

  log "Applying patches for ${package_name} ${package_version}"

  package_apply_patches "${_patches[@]}"

  if ! path_exists "${extension_patches_path}/$(os_type)"
  then
    return 0
  fi

  _patches=($(
  find "${extension_patches_path}/$(os_type)" \
    -mindepth 1 -maxdepth 1 -iname '*.patch' -type f
  ))
  package_apply_patches "${_patches[@]}"

  if path_exists "${extension_patches_path}/$(os_type)/${package_version}"
  then
    _patches=($(
    find "${extension_patches_path}/$(os_type)/${package_version}" \
      -mindepth 1 -maxdepth 1 -iname '*.patch' -type f
    ))

    package_apply_patches "${_patches[@]}"
  else
    return 0
  fi
}

#
# ## package\_apply\_patches
#
# Applies patches found
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Applies any patches found for the current package.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if apply_patches fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_apply_patches
#
# ### Notes
#
package_apply_patches()
{
  local _patch _patches=("$@")

  if array_is_nonempty _patches
  then
    for _patch in "${_patches[@]}"
    do
      log "TODO: patch application is NIY"
    done
  else
    fail "Cannot apply patches as no patches were given."
  fi
}

#
# ## package\_usage
#
# Sets up system level usage configuration for the package
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Updates usage configuration for the given package
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if usage setup fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_usage
#
# ### Notes
#
package_usage() {
  printf "
  Usage:

  $0 [options]

  options:

  --prefix   - specify prefix path
  --src)     - specify source directory
  --data)    - specify data directory
  --user)    - specify user to install as
  --version) - specify version to install
  --licence) - view licence
  --help)    - view this usage information

  "
  return 0
}

#
# ## package\_cli
#
# Parses package CLI arguments
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Updates cli configuration for the given package
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if cli setup fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_cli
#
# ### Notes
#
package_cli()
{
  local _ignored_args=()

  number_of_args=${#extension_args[@]}

  for (( index=0 ; index < $number_of_args ; index++ ))
  do
    token="${extension_args[$index]}"

    case "$token" in
      --prefix)
        packages_path="${extension_args[$((++index))]}"
        ;;
      --src)
        src_path="${extension_args[$((++index))]}"
        ;;
      --data)
        data_path="${extension_args[$((++index))]}"
        ;;
      --user)
        package_user="${extension_args[$((++index))]}"
        ;;
      --version)
        package_version="${extension_args[$((++index))]}"
        ;;
      --base_url)
        package_base_url="${extension_args[$((++index))]}"
        ;;
      --file)
        package_file="${extension_args[$((++index))]}"
        ;;
      --static)
        static_flag=1
        ;;
      --directory)
        package_directory="${extension_args[$((++index))]}"
        ;;
      --archive_format)
        archive_format="${extension_args[$((++index))]}"
        ;;
      --activate|activate)
        # TODO: Throw error if parameter is not specified.
        if (( ${#extension_args[@]} > 0 ))
        then
          if [[ "${extension}" == "pkg" ]]
          then
            array_shift extension_args
            package_activate "${extension_args[@]}"
          else
            package_activate "${extension}" "${extension_args[@]}"
          fi
          exit $?
        else
          error "Cannot activate ${extension}, no version was given."
        fi
        ;;
      --deactivate|deactivate)
        package_deactivate "${extension}"
        ;;
      --md5)
        package_md5="${extension_args[$((++index))]}"
        ;;
      --licence)
        extension_license
        exit 0
        ;;
      --help)
        package_usage
        exit 0
        ;;
      --with*|--enable*|--disable*)
        configure_flags+=("$token" "${extension_args[$((++index))]}")
        ;;
      --trace)
        set -o xtrace
        ;;
      *)
        _ignored_args+=("${token}")
        ;;
    esac

    if (( ${#_ignored_args[@]} > 0 ))
    then
      extension_args=( "${_ignored_args[@]}" )
    fi

  done

  #if (( static_flag = 1 ))
  #then
  #  configure_flags+=( --static ) # --disable-shared
  #else
  #  configure_flags+=( --enable-shared )
  #fi

  # explicitely reload the package module to pickup any CLI overrides.
  modules_reinitialize package
}

#
# ## package\_active
#
# Parses package active arguments
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Updates active configuration for the given package
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if active setup fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_active
#
# ### Notes
#
package_is_active()
{
  local _name="${1:-}"
  local _version="${2:-}"

  if variable_is_nonempty _name
  then
    shift
    local _path="${packages_path}/${_name}"

    if variable_is_nonempty _version
    then
      shift
      if [[ -d "${_path}/${_version}" ]]
      then
        return 0
      else
        return 1
      fi
    else
      if [[ -L "${_path}/active" && -d $(readlink "${_path}/active") ]]
      then
        return 0
      else
        return 1
      fi
    fi

  else
    fail "Can not query if a package is active as no package name was given or name is empty."
  fi
}

#
# ## package\_must\_be\_active
#
# Parses package must be arguments
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Updates must be configuration for the given package
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if must be setup fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_must be
#
# ### Notes
#
package_must_be_active()
{
  local _name="${1:-}"

  if variable_is_nonempty _name
  then
    if package_is_active "${_name}"
    then
      return 0
    else
      error "Install/activate the node package extension before installing ${_name}."
    fi
  else
    fail "Can not ensure that package is active as no package name was given or name is empty."
  fi
}

#
# ## packages\_must\_be\_active
#
# Parses package must_be_active arguments
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# Updates must_be_active configuration for the given package
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Errors halting program if must_be_active setup fails for any reason.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ packages_must_be_active
#
# ### Notes
#
packages_must_be_active()
{
  local _package _packages="$@"

  if array_is_nonempty _packages
  then
    for _package in "${_packages[@]}"
    do
      package_must_be_active "${_package}"
    done
  else
    fail "Can not ensure that packages are active as no packages were given."
  fi
}

#
# ## package\_docs
#
# Opens package documentation website either in the web browser (if able) or via
# curl through PAGER (defaulting to less).
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# None if can open web browser or website docs in PAGER otherwise.
#
# ### Environmental effects
#
# Web browser will open with documentation url if able.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Error if package_docs_url has not been set.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_docs
#
# ### Notes
#
package_docs()
{
  # TODO: move this logic into a core dsl function.
  if os_is_darwin
  then
    open $package_docs_url

  elif command_exists xdg-open
  then
    xdg-open $package_docs_url

  elif command_exists lynx
  then
    lynx $package_docs_url

  elif command_exists links
  then
    links $package_docs_url

  else
    curl -s -L $package_docs_url -o /tmp/${package_name}-${package_version}.docs
    ${EDITOR:-/bin/less} /tmp/${package_name}-${package_version}.docs
  fi
}

#
# ## package\_website
#
# Opens package documentation website either in the web browser (if able) or via
# curl through PAGER (defaulting to less).
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# None if can open web browser or website website in PAGER otherwise.
#
# ### Environmental effects
#
# Web browser will open with documentation url if able.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Error if package_website_url has not been set.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_website
#
# ### Notes
#
package_website()
{
  # TODO: move this logic into a core dsl function.
  if os_is_darwin
  then
    open $package_website_url

  elif command_exists xdg-open
  then
    xdg-open $package_website_url

  elif command_exists lynx
  then
    lynx $package_website_url

  elif command_exists links
  then
    links $package_website_url

  else
    curl -s -L $package_website_url \
      -o /tmp/${package_name}-${package_version}.website

    ${EDITOR:-/bin/less} /tmp/${package_name}-${package_version}.website
  fi
}

package_dependencies()
{
  local dependency _dependencies=(${package_dependencies[@]})

  unset package_dependencies

  # TODO: improve dependency management.
  for dependency in "${_dependencies[@]}"
  do
    if ! package_is_active ${dependency/-/ }
    then
      error "${extension} requires ${dependency}, please install the ${dependency} via bdsm package module and retry.
Typically this would be done as follows:

sudo bdsm ${dependency} package install

      "
    fi
  done
}


#
# ## package\_info
#
# Displays package information.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# Steps performed are logged to STDOUT of the calling environment.
#
# ### Environmental effects
#
# Package installation artifacts are created in the system and source directories.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Fails if any of the constituant components fail.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ package_info "bash"
#
# default_version: "4.2"
# base_url "ftp.gnu.org/gnu/bash/"
# ...
#
# user$ package_info
#
# ### Notes
#
package_info()
{
  local _path="${packages_path}/${package_name}"
  : \
    "${package_file:="${package_name}-${package_version}.${archive_format}"}" \
    "${package_dir:="${package_name}-${package_version}"}" \
    "${package_url:="${package_base_url}/${package_file}"}"

  log "versions:"
  if path_exists "${_path}"
  then
    local _installed=($(
    enter "${_path}"
    find . -mindepth 1 -maxdepth 1 -type d | sed -e 's#./##g' -e 's# #,#g'
    ))
    log "  installed: ${_installed[*]}"
  else
    log "  installed: none"
  fi
  log "  default: ${package_version}"

  if [[ -d "${_path}" ]] && [[ -L "${_path}/active" ]]
  then
    log "  active: $(readlink "${_path}/active")"
  else
    log "  active: none"
  fi

  log "urls:"
  log "  base: ${package_base_url}"
  log "  download: ${package_url}"
  log "archive: ${package_file}"
  log "source_path: ${source_path}/${package_dir}"

  if array_is_nonempty package_dependencies
  then
    log "dependencies: ${package_dependencies[*]}"
  fi
}


