#!/bin/bash

#################################################################
#                                                               #
#             GNOME Shell Extension Installer v1.2.1            #
#                                                               #
#  A simple (scriptable) way to install GNOME Shell Extensions! #
#                                                               #
#       Author: Cyrus Frost                                     #
#       License: MIT                                            #
#                                                               #
#     https://github.com/cyfrost/install-gnome-extensions       #
#                                                               #
#################################################################

#vars
script_revision="v1.2.1"
args_count="$#"
dependencies=(wget curl jq unzip tput sed egrep sed awk gnome-shell cut basename)
deps_install_apt="sudo apt install -y wget curl jq unzip sed"
deps_install_dnf="sudo dnf install -y wget curl jq unzip sed"
EXTENSIONS_TO_INSTALL=()
OVERWRITE_EXISTING=false
ENABLE_ALL=false
INSTALLED_EXT_COUNT=''
INSTALLED_EXTs=''

# message colors.
info_text_blue=$(tput setaf 7)
normal_text=$(tput sgr0)
error_text=$(tput setaf 1)
status_text_yellow=$(tput setaf 3)

# Bail immediately if running as root.
function CheckIfRunningAsRoot() {
  if [ "$(id -u)" = 0 ]; then
    printf "
%s Running this script as root is discouraged and won't work since it needs user directories to operate. Retry as normal user.
Note: If you're trying to install extensions for another user on this computer, try 'su <user_account_name>' and proceed.
Abort.
%s" "$error_text" "$normal_text"

    exit 1
  fi
}

# Bail immediately if running as root.
CheckIfRunningAsRoot

# Trap SIGINT and SIGTERM.
function _term() {
  printf "\n\n%s" "$normal_text"
  trap - INT TERM # clear the trap
  kill -- -$$
}

# Trap SIGINT and SIGTERM for cleanup.
trap _term INT TERM

# This function can check for binaries/commands to be available in Env PATH and report otherwise.
function CheckDependencies() {

  # echo -en "\n${info_text_blue}Checking dependencies...${normal_text}";
  dependencies=("$@")
  for name in "${dependencies[@]}"; do
    command -v "$name" >/dev/null 2>&1 || {
      echo -en "${error_text}\n[Error] Command not found: \"$name\"${normal_text}"
      deps=1
    }
  done
  [[ $deps -ne 1 ]] || {
    echo -en "${error_text}\n\nOne or more dependencies is unavailable. Please make sure the above commands are available and re-run this script.\n\n${status_text_yellow}For Ubuntu and similar distros, try: $deps_install_apt\n\nFor Fedora and similar distros, try: $deps_install_dnf\n\n${normal_text}"
    exit 1
  }
}

# Fail if dependencies unmet.
CheckDependencies "${dependencies[@]}"

function confirm_action() {
  while true; do
    printf "\n%s" "$normal_text"
    read -r -p "$1" -n 1 yn
    case $yn in
    [Yy]*) return 0 ;;
    [Nn]*) return 1 ;;
    *) printf "\nPlease answer with 'y' or 'n'." ;;
    esac
  done
}

# check if current (active) desktop instance is GNOME.
function IsEnvGNOME() {

  if [ "$XDG_CURRENT_DESKTOP" = "" ]; then
    desktop=$(echo "$XDG_DATA_DIRS" | grep -Po '(xfce|kde|gnome)' | tail -n 1)
    #sed 's/.*\(xfce\|kde\|gnome\).*/\1/')
  else
    desktop=$XDG_CURRENT_DESKTOP
  fi

  desktop=${desktop,,}

  if [[ $desktop == *"gnome"* ]]; then
    return 0
  else
    return 1
  fi
}

function enable_extension() {
  ext_uuid="$1"
  gnome-extensions enable "$ext_uuid" >/dev/null 2>&1
}

function disable_extension() {
  ext_uuid="$1"
  gnome-extensions disable "$ext_uuid" >/dev/null 2>&1
}

function get_installed_extensions_list() {
  user_extensions_path="/home/$USER/.local/share/gnome-shell/extensions"
  array=()
  while IFS="" read -r line; do array+=("$line"); done < <(find "$user_extensions_path" -maxdepth 1 -mindepth 1 -type d -not -path '*/\.*' -printf "%f\n")
  ext_list=$(printf "'%s'," "${array[@]}")
  ext_list=${ext_list%,}
  INSTALLED_EXT_COUNT="${#array[@]}"
  INSTALLED_EXTs=$(printf '%s\n' "${array[@]}")
  # echo $ext_list
}

function install_shell_extensions() {

  for ext_id in "${EXTENSIONS_TO_INSTALL[@]}"; do

    request_url="https://extensions.gnome.org/extension-info/?pk=$ext_id&shell_version=$GNOME_SHELL_VERSION"
    http_response="$(curl -s -o /dev/null -I -w "%{http_code}" "$request_url")"

    if [ "$http_response" = 404 ]; then
      printf "\n%sError: No extension exists matching the ID: %s and GNOME Shell version %s (Skipping this).\n" "$error_text" "$ext_id" "$GNOME_SHELL_VERSION"
      continue
    fi

    printf "%s\n" "$normal_text"
    ext_info="$(curl -s "$request_url")"
    extension_name="$(echo "$ext_info" | jq -r '.name')"
    direct_dload_url="$(echo "$ext_info" | jq -r '.download_url')"
    ext_uuid="$(echo "$ext_info" | jq -r '.uuid')"
    ext_version="$(echo "$ext_info" | jq -r '.version')"
    ext_homepage="$(echo "$ext_info" | jq -r '.link')"
    ext_description="$(echo "$ext_info" | jq -r '.description')"
    download_url="https://extensions.gnome.org"$direct_dload_url
    target_installation_dir="/home/$USER/.local/share/gnome-shell/extensions/$ext_uuid"
    printf "%s\nDownloading and installing extension \"%s\"%s" "$status_text_yellow" "$extension_name" "$normal_text"
    printf "%s" "$info_text_blue"
    printf "\nDescription: %s" "$ext_description"
    printf "\nExtension ID: %s" "$ext_id"
    printf "\nExtension Version: v%s" "$ext_version"
    printf "\nHomepage: https://extensions.gnome.org%s" "$ext_homepage"
    printf "\nUUID: \"%s\"" "$ext_uuid"
    printf "\nInstalling to: \"%s\"" "$target_installation_dir"

    if [ -d "$target_installation_dir" ] && [ "$OVERWRITE_EXISTING" = "false" ]; then
      confirm_action "${normal_text}This extension is already installed. Do you want to overwrite it? (y/n): " || continue
    fi

    printf "\n%sPlease wait...\n" "$info_text_blue"
    filename="$(basename "$download_url")"
    wget -q "$download_url" \
      && mkdir -p "$target_installation_dir" \
      && unzip -o -q "$filename" -d "$target_installation_dir" \
      && sleep 1 \
      && rm "$filename"
    schemas_dir="$target_installation_dir/schemas"

    if [ -d "$schemas_dir" ]; then
      printf "%sInstall GLib Schemas for dconf storage, this step will require sudo access...\n" "$info_text_blue"
      cd "$schemas_dir" || {
        echo "$0: Directory do not exist"
        exit 1
      }
      sudo cp ./*.xml "/usr/share/glib-2.0/schemas/"
      sudo glib-compile-schemas /usr/share/glib-2.0/schemas/ >/dev/null 2>&1
    fi

    if [ ! "$ENABLE_ALL" = "false" ]; then
      enable_extension "$ext_uuid"
    fi

    printf "%sDone!\n%s" "$info_text_blue" "$normal_text"
  done
  printf "\n"
}

# Check if arg is number.
function IsNumber() {
  re='^[0-9]+$'
  if [[ "$1" =~ $re ]]; then
    return 0
  fi
  return 1
}

function print_usage() {
  print_banner

  #    -u, --update        Updates existing extensions to latest available versions.
  printf "
Usage: ./install-gnome-extensions.sh [options] <extension_ids> | [links_file]
Options:
    -e, --enable        Enable extension after installing it.
    -o, --overwrite     Overwrites existing extensions.
    -l. --list          Lists the UUIDs of installed extensions.
    -f, --file          Specify a file containing extension links to install.
    -h, --help          Display this help message.
Example usages:
---------------
1) ./install-gnome-extensions.sh 6 8 19 --enable

    Installs and enables extensions with IDs 6, 8, and 19.
2) ./install-gnome-extensions.sh -e --file links.txt
    Installs and enables the extensions from the URLs specified in \"links.txt\" file.
"
}

function print_banner() {
  printf "%s
===========================================================
    GNOME Shell Extensions Installer %s
A simple (scriptable) way to install GNOME Shell extensions.
https://github.com/cyfrost/install-gnome-extensions
===========================================================\n" "$normal_text" "$script_revision"
}

function trim_file() {
  file="$1"
  sed -i '/^[[:blank:]]*$/ d' "$file" && awk '{$1=$1};1' "$file" >tmp && mv tmp "$file"
}

function install_exts_from_links_file() {

  file="$1"
  if [ ! -f "$file" ] || [ ! -s "$file" ]; then
    printf "\n%sError: Supplied argument (\"%s\") is either not a valid file or is empty. %s\n\nPlease gather all extension links in a text file (line-by-line) and try again.\n\nSample usage: ./install-gnome-extensions --file links.txt\n\n" "$error_text" "$1" "$normal_text"
    exit 1
  fi

  trim_file "$file"

  printf "\nParsing file \"%s\" for extension links...\n" "$file"

  while IFS="" read -r p || [ -n "$p" ]; do
    url="$(echo "$p" | sed '/^[[:space:]]*$/d')"
    ext_id="$(echo "$url" | tr '\n' ' ' | sed -e 's/[^0-9]/ /g' -e 's/^ *//g' -e 's/ *$//g' | tr -s ' ' | awk '{print $1;}')"
    IsNumber "$ext_id" && EXTENSIONS_TO_INSTALL+=("$ext_id") || printf "\n%sError: Invalid URL: %s (Skipping this).%s" "$error_text" "$url" "$normal_text"
  done <"$file"
  printf "\n"
}

function begin_install() {

  exts_list="$(printf '%s, ' "${EXTENSIONS_TO_INSTALL[@]}")"
  exts_list=${exts_list%, }

  print_banner
  printf "\n%s[Info] Detected GNOME Shell version: %s\n\nInstalling %s extensions (%s)...\n%s" "$info_text_blue" "$GNOME_SHELL_VERSION" "$extensions_count" "$exts_list" "$normal_text"
  install_shell_extensions
  printf "\n%sComplete!\n\n" "$normal_text"
  IsEnvGNOME || printf "%sPlease login to GNOME desktop to see the installed/enabled extensions.\n\n" "$normal_text"
}

# Obtain GNOME Shell version.
GNOME_SHELL_VERSION="$(gnome-shell --version | cut --delimiter=' ' --fields=3 | cut --delimiter='.' --fields=1,2)"

while test $# -gt 0; do
  case "$1" in
  -e | --enable)
    ENABLE_ALL=true
    ;;
    #        -u|--update)
    #            UPDATE=true
    #            ;;
  -o | --overwrite)
    OVERWRITE_EXISTING=true
    ;;
  -h | --help)
    print_usage
    exit 0
    ;;
  -l | --list)
    get_installed_extensions_list
    printf "\n============================\nInstalled extensions (UUIDs)\n============================\n\n%s\n\n%s extensions are installed.\n\nDone!\n\n" "$INSTALLED_EXTs" "$INSTALLED_EXT_COUNT"
    exit 0
    ;;
  -f | --file)
    install_exts_from_links_file "$2"
    ;;
  esac
  IsNumber "$1" && EXTENSIONS_TO_INSTALL+=("$1")
  shift
done

extensions_count="${#EXTENSIONS_TO_INSTALL[@]}"

if [ "$args_count" -eq 0 ]; then
  printf "\n%sInvalid usage.\n%s" "$error_text" "$normal_text"
  print_usage

elif [ "$extensions_count" -eq 0 ]; then
  printf "\n%sError: Could not find any valid extension IDs or URLs for installation.\n%s\n" "$error_text" "$normal_text"
  exit 1
else
  begin_install
fi
