#!/usr/bin/env bash
#
# Script to generate Regolith profile for gnome-terminal.
# Based on ~
#
# Copyright (c) 2016-present Arctic Ice Studio <development@arcticicestudio.com>
# Copyright (c) 2016-present Sven Greb <code@svengreb.de>

# Project:    Nord GNOME Terminal
# Repository: https://github.com/arcticicestudio/nord-gnome-terminal
# License:    MIT

set -e

# Appends the given profile UUID to the profile list.
#
# @param $1 the UUID to be appended
# @return none
# @since 0.2.0
append_profile_uuid_to_list() {
  local uuid list
  uuid="$1"
  list=$(gsettings get "$GSETTINGS_PROFILELIST_PATH" list)
  gsettings set "$GSETTINGS_PROFILELIST_PATH" list "${list%]*}, '$uuid']"
}

# Writes the Regolith GNOME Terminal theme colors and configurations as dconf key-value pairs to the target profile.
#
# @globread profile_name
# @return none
# @since 0.2.0
apply() {
  resource_getter="xrescat"
  if [ $XDG_SESSION_TYPE == "wayland" ]; then 
    resource_getter="trawlcat"
  fi

  local \
    color0="$($resource_getter gnome.terminal.color0)" \
    color1="$($resource_getter gnome.terminal.color1)" \
    color2="$($resource_getter gnome.terminal.color2)" \
    color3="$($resource_getter gnome.terminal.color3)" \
    color4="$($resource_getter gnome.terminal.color4)" \
    color5="$($resource_getter gnome.terminal.color5)" \
    color6="$($resource_getter gnome.terminal.color6)" \
    color7="$($resource_getter gnome.terminal.color7)" \
    color8="$($resource_getter gnome.terminal.color8)" \
    color9="$($resource_getter gnome.terminal.color9)" \
    color10="$($resource_getter gnome.terminal.color10)" \
    color11="$($resource_getter gnome.terminal.color11)" \
    color12="$($resource_getter gnome.terminal.color12)" \
    color13="$($resource_getter gnome.terminal.color13)" \
    color14="$($resource_getter gnome.terminal.color14)" \
    color15="$($resource_getter gnome.terminal.color15)" \
    font="$($resource_getter gnome.terminal.font "monospace")" \
    scrollbar="$($resource_getter gnome.terminal.scrollbar never)" \
    useTransparentBg="$($resource_getter gnome.terminal.use-transparent-background false)" \
    audibleBell="$($resource_getter gnome.terminal.audible-bell false)" \
    transparentBgPercent="$($resource_getter gnome.terminal.background-transparency-percent 4)" \
    blink="$($resource_getter gnome.terminal.cursor-blink-mode off)"

  _write palette "['$color0', '$color1', '$color2', '$color3', '$color4', '$color5', '$color6', '$color7', '$color8', '$color9', '$color10', '$color11', '$color12', '$color13', '$color14', '$color15']"
  log 4 "Applied color color palette"

  local background="$($resource_getter gnome.terminal.background-color $color0)"
  local foreground="$($resource_getter gnome.terminal.foreground-color $color7)"
  _write background-color "'$background'"
  _write foreground-color "'$foreground'"
  _write use-transparent-background "$useTransparentBg"
  _write background-transparency-percent "$transparentBgPercent"
  log 4 "Applied background- and foreground colors"

  _write bold-color "'$color4'"
  _write bold-color-same-as-fg "false"
  _write bold-is-bright "false"
  log 4 "Applied bold color and configuration"

  _write use-theme-colors "false"
  _write use-theme-background "false"
  _write use-theme-transparency "false"
  _write use-system-font "false"
  log 4 "Applied system theme compability configuration"

  _write cursor-colors-set "true"
  _write cursor-foreground-color "'$color1'"
  _write cursor-background-color "'$color8'"
  log 4 "Applied cursor colors and configuration"

  _write highlight-colors-set "true"
  _write highlight-foreground-color "'$color0'"
  _write highlight-background-color "'$color8'"
  log 4 "Applied highlight colors and configuration"

  _write cursor-blink-mode "'$blink'"
  _write audible-bell "$audibleBell"
  _write font "'$font'"
  _write scrollbar-policy "'$scrollbar'"

  gsettings set org.gnome.Terminal.Legacy.Settings default-show-menubar false
  gsettings set org.gnome.Terminal.Legacy.Settings headerbar "@mb false"
  gsettings set org.gnome.Terminal.ProfilesList default "$profile_uuid"

  _write "$NORD_GNOME_TERMINAL_VERSION_DCONF_KEY" "'$NORD_GNOME_TERMINAL_VERSION'"
  log 4 "Set Regolith GNOME Terminal version key of the '$profile_name' profile"
}

# Applies the Regolith GNOME Terminal theme with version comparison.
#
# @param $1 the version string to compare against
# @return none
# @since 0.2.0
apply_version_compared() {
  local version
  version=$1
  case "$(vercomp "$NORD_GNOME_TERMINAL_VERSION" "$version"; echo $?)" in
    0)
      log 3 "Reinstalling Regolith GNOME Terminal since the version equaled the version of the '$profile_name' profile!";
      apply;
      log 2 "Reinstalled Regolith GNOME Terminal version $NORD_GNOME_TERMINAL_VERSION for the '$profile_name' profile!";
      exit 0;;
    1)
      log 4 "The script version is newer than the currently installed theme ($NORD_GNOME_TERMINAL_VERSION > $version)"
      apply;
      log 2 "The '$profile_name' profile has been updated successfully from version $version to $NORD_GNOME_TERMINAL_VERSION";
      exit 0;;
    2)
      log 1 "The detected Regolith GNOME Terminal version $version of the '$profile_name' profile is greater than the version that will be applied!";
      printf "${_ctb}> [?]${_ct} Override current profile version %s with script version %s? (${_ctb}y${_cr}/${_ctb}n${_cr})" "$version" "$NORD_GNOME_TERMINAL_VERSION"
      read -r -n 1 -s confirmation;
      echo
      case $confirmation in
        [Yy]* )
          apply;
          log 2 "Regolith GNOME Terminal version $NORD_GNOME_TERMINAL_VERSION has been successfully applied!";
          exit 0;;
        [Nn]* )
          log 0 "Installation canceled by user!";
          exit 1;;
        * )
          log 0 "'$confirmation' is not a valid input!";
          exit 1;;
      esac
  esac
}

# Checks the GNOME Terminal version for the required migration compability (>= 3.8).
#
# @globwrite gnome_terminal_version
# @return 0 if the version is compatible (>= 3.8), 1 otherwise
# @since 0.2.0
check_migrated_version_comp() {
  gnome_terminal_version="$(expr "$(LANGUAGE=en_US.UTF-8 gnome-terminal --version)" : '^[^[:digit:]]* \(\([[:digit:]]*\.*\)*\)')"
  if [[ ("$(echo "$gnome_terminal_version" | cut -d"." -f1)" = "3" && \
         "$(echo "$gnome_terminal_version" | cut -d"." -f2)" -ge 8) || \
         "$(echo "$gnome_terminal_version" | cut -d"." -f1)" -ge 4 ]]; then
    log 3 "Detected compatible GNOME Terminal version $gnome_terminal_version (>= 3.8 dconf migrated)"
    return 0
  else
    log 1 "Detected incompatible GNOME Terminal version $gnome_terminal_version (< 3.8)"
    return 1
  fi
}

# Cleans up the script execution by unsetting declared functions and variables.
#
# @return none
# @since 0.2.0
cleanup() {
  log 4 "Cleaning up script execution by unsetting declared functions and variables"
  unset -v _cr _ct _ctb _ct_highlight _ct_primary _ctb_error _ctb_highlight _ctb_primary _ctb_success _ctb_warning
  unset -v NORD_GNOME_TERMINAL_SCRIPT_OPTS NORD_GNOME_TERMINAL_VERSION NORD_GNOME_TERMINAL_VERSION_DCONF_KEY NORD_PROFILE_VISIBLE_NAME log_level DEPENDENCIES DCONF_PROFILE_BASE_PATH GSETTINGS_PROFILELIST_PATH gnome_terminal_version profile_name profile_uuid
  unset -f append_profile_uuid_to_list apply check_migrated_version_comp cleanup clone_default_profile get_profiles get_profile_uuid_by_name log print_help validate_dependencies vercomp _write
}

# Clones the default profile, generates and saves the new UUID and adds it to the profile list.
#
# @globwrite profile_uuid
# @since 0.2.0
clone_default_profile() {
  local uuid
  uuid="$(gsettings get "$GSETTINGS_PROFILELIST_PATH" default | tr -d \')"
  profile_uuid="$(uuidgen)"
  dconf dump "$DCONF_PROFILE_BASE_PATH"/:"$uuid"/ | dconf load "$DCONF_PROFILE_BASE_PATH"/:"$profile_uuid"/
  dconf write "$DCONF_PROFILE_BASE_PATH"/:"$profile_uuid"/visible-name "'$NORD_PROFILE_VISIBLE_NAME'"
  append_profile_uuid_to_list "$profile_uuid"
  log 3 "Cloned the default profile '$uuid' with new UUID '$profile_uuid'"
}

# Gets the current Regolith GNOME Terminal version.
#
# @globread NORD_GNOME_TERMINAL_VERSION_DCONF_KEY DCONF_PROFILE_BASE_PATH # @param $1 the UUID of the profile to check
# @since 0.2.0
get_current_version() {
  local uuid version
  uuid=$1
  version=
  printf "$(dconf read $DCONF_PROFILE_BASE_PATH/:"$uuid"/$NORD_GNOME_TERMINAL_VERSION_DCONF_KEY | tr -d "'")"
  return 0
}

# Gets the available GNOME Terminal profiles.
#
# @globwrite profiles
# @return none
# @since 0.2.0
get_profiles() {
  profiles=($(gsettings get "$GSETTINGS_PROFILELIST_PATH" list | tr -d "[]\',"))
  log 3 "Available profile UUIDs: ${profiles[*]}"
}

# Gets the UUID for the given profile name.
#
# @param $1 the name of the profile to get the UUID from
# @return the UUID of the profile, none otherwise
# @since 0.2.0
get_profile_uuid_by_name() {
  local name=$1
  for idx in "${!profiles[@]}"; do
    if [[ "$(dconf read "$DCONF_PROFILE_BASE_PATH"/:"${profiles[idx]}"/visible-name)" == "'$name'" ]]; then
      printf "%s" "${profiles[idx]}"
      return 0
    fi
  done
}

# Prints a message with a prefixed label to STDOUT/STDERR for the given log level.
#
# When no log level is specified, "DEFAULT" is used.
# The minimum log level is defined by the "log_level" global.
#
# Log Levels:
#   0 ERROR
#   1 WARNING
#   2 SUCCESS
#   3 INFO
#   4 DEBUG
#
# @globread
#   log_level
#   _cr
#   _ct
#   _ctb
#   _ctb_error
#   _ctb_highlight
#   _ctb_primary
#   _ctb_success
#   _ctb_warning
# @return none
# @since 0.2.0
log () {
  declare -a label color
  local num_regex='^[0-9]+$'
  local level=$1
  label=([0]="[ERR]" [1]="[WARN]" [2]="[SUCCESS]" [3]="[INFO]" [4]="[DEBUG]")
  color=([0]="$_ctb_error" [1]="$_ctb_warning" [2]="$_ctb_success" [3]="$_ctb_primary" [4]="$_ctb_highlight")

  if [[ $level =~ $num_regex ]]; then
    shift
    if [[ -n ${log_level} && ${log_level} -ge ${level} ]]; then
      printf "${color[$level]}${label[$level]} ${_ct}%s${_cr}\n" "$@"
    fi
  else
    printf "${_ctb}> ${_ct}%s${_cr}\n" "$@"
  fi
}

# Prints the help- and usage information.
#
# @return none
# @since 0.2.0
print_help() {
  echo -e -n "\
  \r${_ctb}Usage: ${_ct_primary}${0##*/} ${_ctb_subtle}[OPTIONS]
  ${_ctb_highlight}-h${_ct},${_ctb_highlight} --help ${_ct}
  Show the help

  ${_ctb_highlight}-l${_ct},${_ctb_highlight} --loglevel <LOG_LEVEL>${_ct},${_ctb_highlight} --loglevel=<LOG_LEVEL> ${_ct}
  Set the log level
    ${_ctb_primary}0 ${_ctb}ERROR${_cr} The script will run in silent mode, only error messages are shown
    ${_ctb_primary}1 ${_ctb}WARNING${_cr} Shows warning messages
    ${_ctb_primary}2 ${_ctb}SUCCESS${_cr} Shows success messages (default)
    ${_ctb_primary}3 ${_ctb}INFO${_cr} Shows additional information messages
    ${_ctb_primary}4 ${_ctb}DEBUG${_cr} Runs the script in debug mode showing additional debug messages

  ${_ctb_highlight}-p${_ct},${_ctb_highlight} --profile <PROFILE_NAME>${_ct},${_ctb_highlight} --profile=<PROFILE_NAME>${_ct}
  The name of the profile to install the theme to. If not specified a new profile as clone of the 'default' profile will be created.${_cr}\n"
}

# Validates all required dependencies.
#
# @param $1 array of required dependencies to validate
# @return 0 if all required dependencies are validated, 1 otherwise
# @since 0.2.0
validate_dependencies() {
  declare -a missing_deps deps=("${!1}")
  for exec in "${deps[@]}"; do
    if ! command -v "${exec}" > /dev/null 2>&1; then
      missing_deps+=(${exec})
    fi
  done
  if [ ${#missing_deps[*]} -eq 0 ]; then
    log 3 "Validated required dependencies: ${deps[*]}"
    return 0
  else
    log 1 "Missing required dependencies: ${_ct_highlight}${missing_deps[*]}${_cr}"
    return 1
  fi
}

# Compares whether the given version string is greater than, equal to or less than the given comparative version string.
#
# @param $1 the version string to be compared
# @param $2 the version string to compare against
# @return 0 if the versions are equal, 1 if the version is greater or 2 if the version is less than the comparative
# version
# @ since 0.2.0
vercomp() {
  # v1 equals v2
  if [[ "$1" == "$2" ]]; then
    return 0
  fi

  local IFS=.
  local i v1=($1) v2=($2)

  # Fill empty fields with zeros
  for ((i=${#v1[@]}; i<${#v2[@]}; i++)); do
    v1[i]=0
  done

  for ((i=0; i<${#v1[@]}; i++)); do
    if [[ -z ${v2[i]} ]]; then
     # Fill empty fields with zeros
      v2[i]=0
    fi

    # v1 is greater than v2
    if ((10#${v1[i]} > 10#${v2[i]})); then
      return 1
    fi
    # v1 is less than v2
    if ((10#${v1[i]} < 10#${v2[i]})); then
      return 2
    fi
  done
  return 0
}

# Shorthand function to write the given key-value pair to the profile.
#
# @globread DCONF_PROFILE_BASE_PATH profile_uuid
# @param $1 the profile key to be written
# @param $2 the value to be assigned to the given profile key
# @return none
# @since 0.2.0
_write() {
  local key="$1"
  local value="$2"
  dconf write "$DCONF_PROFILE_BASE_PATH/:$profile_uuid/$key" "$value"
}

# Catches terminal interrupt- and termination signals and prints a message before exiting the script execution.
#
# @return 1
# @since 0.2.0
trap 'printf "${_ctb_error}User aborted.${_cr}\n" && exit 1' SIGINT SIGTERM

# Exit hook that runs the 'cleanup' function before exiting the script.
#
# @since 0.2.0
trap cleanup EXIT

declare -a DEPENDENCIES profiles

_cr="\e[0m"
_ct="\e[0;37m"
_ctb="\e[1;37m"
_ct_highlight="\e[0;34m"
_ct_primary="\e[0;36m"
_ctb_error="\e[1;31m"
_ctb_highlight="\e[1;34m"
_ctb_primary="\e[1;36m"
_ctb_subtle="\e[1;30m"
_ctb_success="\e[1;32m"
_ctb_warning="\e[1;33m"

NORD_GNOME_TERMINAL_SCRIPT_OPTS=$(getopt -o hl:p: --long help,loglevel:,profile: -n 'nord.sh' -- "$@")
NORD_GNOME_TERMINAL_VERSION=0.1.0
NORD_GNOME_TERMINAL_VERSION_DCONF_KEY=nord-gnome-terminal-version
NORD_PROFILE_VISIBLE_NAME="Regolith"
log_level=2

# List of required executable dependencies
DEPENDENCIES=(dconf expr gsettings uuidgen)

# The dconf- and GSettings paths
DCONF_PROFILE_BASE_PATH=/org/gnome/terminal/legacy/profiles:
GSETTINGS_PROFILELIST_PATH=org.gnome.Terminal.ProfilesList

# The detected GNOME Terminal version
gnome_terminal_version=

# The profile name and UUID to apply the theme on
profile_name=
profile_uuid=

eval set -- "$NORD_GNOME_TERMINAL_SCRIPT_OPTS"
while true; do
  case "$1" in
    --loglevel=* ) log_level=${1#*=}; shift ;;
    -l | --loglevel ) log_level=$2; shift ;;
    -h | --help ) print_help; exit 0; break ;;
    --profile=* ) profile_name=${1#*=}; shift ;;
    -p | --profile ) profile_name=$2; shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
  shift
done

if validate_dependencies DEPENDENCIES[@]; then

  if ! check_migrated_version_comp; then
    log 0 "The installed GNOME Terminal version '$gnome_terminal_version' is not compatible with the required (dconf migrated) version >= 3.8!"
    exit 1
  fi

  # Check for available profiles and validate that at least the default profile is available
  get_profiles
  if [[ ${#profiles[*]} -eq 0 ]]; then
    log 1 "No profiles found!"
    log 0 "There must be at least one default profile!"
    exit 1
  fi

  # Validate- and resolve the UUID if a profile name has been passed
  if [[ -n $profile_name ]]; then
    profile_uuid="$(get_profile_uuid_by_name "$profile_name")"
    if [[ -n $profile_uuid ]]; then
      log 3 "Resolved profile '$profile_name' to UUID '$profile_uuid'"
      curr_ver="$(get_current_version "$profile_uuid")"
      if [[ -n $curr_ver ]]; then
        apply_version_compared "$curr_ver"
      else
        apply
        log 2 "Regolith GNOME Terminal version $NORD_GNOME_TERMINAL_VERSION has been successfully applied to the '$profile_name' profile"
        exit 0
      fi
    else
      log 0 "$profile_name is not a valid profile name!";
      exit 1
    fi
  fi

  # Check for an already existing 'Regolith' profile and update it
  profile_uuid="$(get_profile_uuid_by_name $NORD_PROFILE_VISIBLE_NAME)"
  if [[ -n $profile_uuid ]]; then
    profile_name="$NORD_PROFILE_VISIBLE_NAME"
    log 4 "Found already existing '$NORD_PROFILE_VISIBLE_NAME' profile with UUID '$profile_uuid'"
    log "Updating already existing '$NORD_PROFILE_VISIBLE_NAME' profile"
    apply_version_compared "$(get_current_version $profile_uuid)"
  fi

  clone_default_profile
  apply
  log 2 "Regolith GNOME Terminal version $NORD_GNOME_TERMINAL_VERSION has been successfully applied to the newly created '$NORD_PROFILE_VISIBLE_NAME' profile"
  exit 0
else
  log 0 "Required dependencies were not fulfilled: ${DEPENDENCIES[*]}"
  exit 1
fi
