#!/usr/bin/env bash

##########################################################################
# The MIT License
#
# Copyright (c) jomo
#
# Permission is hereby granted, free of charge,
# to any person obtaining a copy of this software and
# associated documentation files (the "Software"), to
# deal in the Software without restriction, including
# without limitation the rights to use, copy, modify,
# merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom
# the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice
# shall be included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
##########################################################################

# https://github.com/jomo/imgur-screenshot
# https://help.imgur.com/hc/en-us/articles/209592766-Tools-for-Imgur
#
# Slightly modified for `nnn` integration
#
# Shell: Bash
# Description: Upload an image file to imgur

if [ "${1}" = "--debug" ]; then
  echo "########################################"
  echo "Enabling debug mode"
  echo "Please remove credentials before pasting"
  echo "########################################"
  echo ""
  uname -a
  for arg in ${0} "${@}"; do
    echo -n "'${arg}' "
  done
  echo -e "\n"
  shift
  set -x
fi

current_version="v1.7.4"

function is_mac() {
  uname | grep -q "Darwin"
}

### IMGUR-SCREENSHOT DEFAULT CONFIG ####

# You can override the config in ~/.config/imgur-screenshot/settings.conf

imgur_anon_id="ea6c0ef2987808e"
imgur_icon_path="${HOME}/Pictures/imgur.png"

imgur_acct_key=""
imgur_secret=""
login="false"
album_title=""
album_id=""
credentials_file="${HOME}/.config/imgur-screenshot/credentials.conf"

file_name_format="imgur-%Y_%m_%d-%H:%M:%S.png" # when using scrot, must end with .png!
file_dir="${HOME}/Pictures"

upload_connect_timeout="5"
upload_timeout="120"
upload_retries="1"

# shellcheck disable=SC2034
if is_mac; then
  screenshot_select_command="screencapture -i %img"
  screenshot_window_command="screencapture -iWa %img"
  screenshot_full_command="screencapture %img"
  open_command="open %url"
else
  screenshot_select_command="scrot -s %img"
  screenshot_window_command="scrot %img"
  screenshot_full_command="scrot %img"
  open_command="xdg-open %url"
fi
open="true"

mode="select"
edit_command="gimp %img"
edit="false"
exit_on_album_creation_fail="true"

log_file="${HOME}/.imgur-screenshot.log"

auto_delete=""
copy_url="true"
keep_file="true"
check_update="true"

# NOTICE: if you make changes here, also edit the docs at
# https://github.com/jomo/imgur-screenshot/wiki/Config

# You can override the config in ~/.config/imgur-screenshot/settings.conf

############## END CONFIG ##############

settings_path="${HOME}/.config/imgur-screenshot/settings.conf"
if [ -f "${settings_path}" ]; then
  # shellcheck disable=SC1090
  source "${settings_path}"
fi

# dependency check
if [ "${1}" = "--check" ]; then
  (type grep &>/dev/null && echo "OK: found grep") || echo "ERROR: grep not found"
  if is_mac; then
    if type growlnotify &>/dev/null; then
      echo "OK: found growlnotify"
    elif type terminal-notifier &>/dev/null; then
      echo "OK: found terminal-notifier"
    else
      echo "ERROR: growlnotify nor terminal-notifier found"
    fi
    (type screencapture &>/dev/null && echo "OK: found screencapture") || echo "ERROR: screencapture not found"
    (type pbcopy &>/dev/null && echo "OK: found pbcopy") || echo "ERROR: pbcopy not found"
  else
    (type notify-send &>/dev/null && echo "OK: found notify-send") || echo "ERROR: notify-send (from libnotify-bin) not found"
    (type scrot &>/dev/null && echo "OK: found scrot") || echo "ERROR: scrot not found"
    (type xclip &>/dev/null && echo "OK: found xclip") || echo "ERROR: xclip not found"
  fi
  (type curl &>/dev/null && echo "OK: found curl") || echo "ERROR: curl not found"
  exit 0
fi


# notify <'ok'|'error'> <title> <text>
function notify() {
  if is_mac; then
    if type growlnotify &>/dev/null; then
      growlnotify  --icon "${imgur_icon_path}" --iconpath "${imgur_icon_path}" --title "${2}" --message "${3}"
    else
      terminal-notifier -appIcon "${imgur_icon_path}" -contentImage "${imgur_icon_path}" -title "imgur: ${2}" -message "${3}"
    fi
  else
    if [ "${1}" = "error" ]; then
      notify-send -a ImgurScreenshot -u critical -c "im.error" -i "${imgur_icon_path}" -t 500 "imgur: ${2}" "${3}"
    else
      notify-send -a ImgurScreenshot -u low -c "transfer.complete" -i "${imgur_icon_path}" -t 500 "imgur: ${2}" "${3}"
    fi
  fi
}

function take_screenshot() {
  echo "Please select area"
  is_mac || sleep 0.1 # https://bbs.archlinux.org/viewtopic.php?pid=1246173#p1246173

  cmd="screenshot_${mode}_command"
  cmd=${!cmd//\%img/${1}}

  if ! shot_err="$(${cmd} &>/dev/null)"; then  #takes a screenshot with selection
    echo "Failed to take screenshot '${1}': '${shot_err}'. For more information visit https://github.com/jomo/imgur-screenshot/wiki/Troubleshooting" | tee -a "${log_file}"
    notify error "Something went wrong :(" "Information has been logged"
    exit 1
  fi
}

function check_for_update() {
  # exit non-zero on HTTP error, output only the body (no stats) but output errors, follow redirects, output everything to stdout
  remote_version="$(curl --compressed -fsSL --stderr - "https://api.github.com/repos/jomo/imgur-screenshot/releases" | grep -Em 1 --color 'tag_name":\s*".*"' | cut -d '"' -f 4)"
  if [ -n "$remote_version" ]; then
    if [ ! "${current_version}" = "${remote_version}" ] && [ -n "${current_version}" ] && [ -n "${remote_version}" ]; then
      echo "Update found!"
      echo "Version ${remote_version} is available (You have ${current_version})"
      notify ok "Update found" "Version ${remote_version} is available (You have ${current_version}). https://github.com/jomo/imgur-screenshot"
      echo "Check https://github.com/jomo/imgur-screenshot/releases/${remote_version} for more info."
    elif [ -z "${current_version}" ] || [ -z "${remote_version}" ]; then
      echo "Invalid empty version string"
      echo "Current (local) version: '${current_version}'"
      echo "Latest (remote) version: '${remote_version}'"
    else
      echo "Version ${current_version} is up to date."
    fi
  else
    echo "Failed to check for latest version: ${remote_version}"
  fi
}

function check_oauth2_client_secrets() {
  if [ -z "${imgur_acct_key}" ] || [ -z "${imgur_secret}" ]; then
    echo "In order to upload to your account, register a new application at:"
    echo "https://api.imgur.com/oauth2/addclient"
    echo "Select 'OAuth 2 authorization without a callback URL'"
    echo "Then, set the imgur_acct_key (Client ID) and imgur_secret in your config."
    exit 1
  fi
}

function load_access_token() {
  token_expire_time=0
  # check for saved access_token and its expiration date
  if [ -f "${credentials_file}" ]; then
    # shellcheck disable=SC1090
    source "${credentials_file}"
  fi
  current_time="$(date +%s)"
  preemptive_refresh_time="$((10*60))"
  expired="$((current_time > (token_expire_time - preemptive_refresh_time)))"
  if [ -n "${refresh_token}" ]; then
    # token already set
    if [ "${expired}" -eq "0" ]; then
      # token expired
      refresh_access_token "${credentials_file}"
    fi
  else
    acquire_access_token "${credentials_file}"
  fi
}

function acquire_access_token() {
  check_oauth2_client_secrets
  # prompt for a PIN
  authorize_url="https://api.imgur.com/oauth2/authorize?client_id=${imgur_acct_key}&response_type=pin"
  echo "Go to"
  echo "${authorize_url}"
  echo "and grant access to this application."
  read -rp "Enter the PIN: " imgur_pin

  if [ -z "${imgur_pin}" ]; then
    echo "PIN not entered, exiting"
    exit 1
  fi

  # exchange the PIN for access token and refresh token
  response="$(curl --compressed -fsSL --stderr - \
    -F "client_id=${imgur_acct_key}" \
    -F "client_secret=${imgur_secret}" \
    -F "grant_type=pin" \
    -F "pin=${imgur_pin}" \
    https://api.imgur.com/oauth2/token)"
  save_access_token "${response}" "${1}"
}

function refresh_access_token() {
  check_oauth2_client_secrets
  token_url="https://api.imgur.com/oauth2/token"
  # exchange the refresh token for access_token and refresh_token
  if ! response="$(curl --compressed -fsSL --stderr - \
                    -F "client_id=${imgur_acct_key}" \
                    -F "client_secret=${imgur_secret}" \
                    -F "grant_type=refresh_token" \
                    -F "refresh_token=${refresh_token}" \
                    "${token_url}"
                    )"; then
    # curl failed
    handle_upload_error "${response}" "${token_url}"
    exit 1
  fi
  save_access_token "${response}" "${1}"
}

function save_access_token() {
  if ! grep -q "access_token" <<<"${1}"; then
    # server did not send access_token
    echo "Error: Something is wrong with your credentials:"
    echo "${1}"
    exit 1
  fi

  access_token="$(grep -Eo 'access_token":".*"' <<<"${1}" | cut -d '"' -f 3)"
  refresh_token="$(grep -Eo 'refresh_token":".*"' <<<"${1}" | cut -d '"' -f 3)"
  expires_in="$(grep -Eo 'expires_in":[0-9]*' <<<"${1}" | cut -d ':' -f 2)"
  token_expire_time="$(( $(date +%s) + expires_in ))"

  # create dir if not exist
  mkdir -p "$(dirname "${2}")" 2>/dev/null
  touch "${2}" && chmod 600 "${2}"
  cat <<EOF > "${2}"
access_token="${access_token}"
refresh_token="${refresh_token}"
token_expire_time="${token_expire_time}"
EOF
}

function fetch_account_info() {
  response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/account/me)"
  if grep -Eq '"success":\s*true' <<<"${response}"; then
    username="$(grep -Eo '"url":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
    echo "Logged in as ${username}."
    echo "https://${username}.imgur.com"
  else
    echo "Failed to fetch info: ${response}"
  fi
}

function delete_image() {
  response="$(curl --compressed -X DELETE  -fsSL --stderr - -H "Authorization: Client-ID ${1}" "https://api.imgur.com/3/image/${2}")"
  if grep -Eq '"success":\s*true' <<<"${response}"; then
    echo "Image successfully deleted (delete hash: ${2})." >> "${3}"
  else
    echo "The Image could not be deleted: ${response}." >> "${3}"
  fi
}

function upload_authenticated_image() {
  echo "Uploading '${1}'..."
  title="$(echo "${1}" | rev | cut -d "/" -f 1 | cut -d "." -f 2- | rev)"
  if [ -n "${album_id}" ]; then
    response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -F "title=${title}" -F "image=@\"${1}\"" -F "album=${album_id}" -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/image)"
  else
    response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -F "title=${title}" -F "image=@\"${1}\"" -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/image)"
  fi

  # JSON parser premium edition (not really)
  if grep -Eq '"success":\s*true' <<<"${response}"; then
    img_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
    img_ext="$(grep -Eo '"link":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4 | rev | cut -d "." -f 1 | rev)" # "link" itself has ugly '\/' escaping and no https!
    del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"

    if [ -n "${auto_delete}" ]; then
      export -f delete_image
      echo "Deleting image in ${auto_delete} seconds."
      nohup /bin/bash -c "sleep ${auto_delete} && delete_image ${imgur_anon_id} ${del_id} ${log_file}" &
    fi

    handle_upload_success "https://i.imgur.com/${img_id}.${img_ext}" "https://imgur.com/delete/${del_id}" "${1}"
  else # upload failed
    err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
    test -z "${err_msg}" && err_msg="${response}"
    handle_upload_error "${err_msg}" "${1}"
  fi
}

function upload_anonymous_image() {
  echo "Uploading '${1}'..."
  title="$(echo "${1}" | rev | cut -d "/" -f 1 | cut -d "." -f 2- | rev)"
  if [ -n "${album_id}" ]; then
    response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Client-ID ${imgur_anon_id}" -F "title=${title}" -F "image=@\"${1}\"" -F "album=${album_id}" https://api.imgur.com/3/image)"
  else
    response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Client-ID ${imgur_anon_id}" -F "title=${title}" -F "image=@\"${1}\"" https://api.imgur.com/3/image)"
  fi
  # JSON parser premium edition (not really)
  if grep -Eq '"success":\s*true' <<<"${response}"; then
    img_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
    img_ext="$(grep -Eo '"link":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4 | rev | cut -d "." -f 1 | rev)" # "link" itself has ugly '\/' escaping and no https!
    del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"

    if [ -n "${auto_delete}" ]; then
      export -f delete_image
      echo "Deleting image in ${auto_delete} seconds."
      nohup /bin/bash -c "sleep ${auto_delete} && delete_image ${imgur_anon_id} ${del_id} ${log_file}" &
    fi

    handle_upload_success "https://i.imgur.com/${img_id}.${img_ext}" "https://imgur.com/delete/${del_id}" "${1}"
  else # upload failed
    err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
    test -z "${err_msg}" && err_msg="${response}"
    handle_upload_error "${err_msg}" "${1}"
  fi
}

function handle_upload_success() {
  echo ""
  echo "image  link: ${1}"
  echo "delete link: ${2}"

  if [ "${copy_url}" = "true" ] && [ -z "${album_title}" ]; then
    if is_mac; then
      echo -n "${1}" | pbcopy
    else
      echo -n "${1}" | xclip -selection clipboard
    fi
    echo "URL copied to clipboard"
  fi

  # print to log file: image link, image location, delete link
  echo -e "${1}\t${3}\t${2}" >> "${log_file}"

  notify ok "Upload done!" "${1}"

#  if [ ! -z "${open_command}" ] && [ "${open}" = "true" ]; then
#    open_cmd=${open_command//\%url/${1}}
#    open_cmd=${open_cmd//\%img/${2}}
#    echo "Opening '${open_cmd}'"
#    eval "${open_cmd}"
#  fi
}

function handle_upload_error() {
  error="Upload failed: \"${1}\""
  echo "${error}"
  echo -e "Error\t${2}\t${error}" >> "${log_file}"
  notify error "Upload failed :(" "${1}"
}

function handle_album_creation_success() {
  echo ""
  echo "Album  link: ${1}"
  echo "Delete hash: ${2}"
  echo ""

  notify ok "Album created!" "${1}"

  if [ "${copy_url}" = "true" ]; then
    if is_mac; then
      echo -n "${1}" | pbcopy
    else
      echo -n "${1}" | xclip -selection clipboard
    fi
    echo "URL copied to clipboard"
  fi

  # print to log file: album link, album title, delete hash
  echo -e "${1}\t\"${3}\"\t${2}" >> "${log_file}"
}

function handle_album_creation_error() {
  error="Album creation failed: \"${1}\""
  echo -e "Error\t${2}\t${error}" >> "${log_file}"
  notify error "Album creation failed :(" "${1}"
  if [ ${exit_on_album_creation_fail} ]; then
    exit 1
  fi
}

while [ ${#} != 0 ]; do
  case "${1}" in
  -h | --help)
    echo "usage: ${0} [--debug] [-c | --check | -v | -h | -u]"
    echo "       ${0} [--debug] [option]... [file]..."
    echo ""
    echo "      --debug                  Enable debugging, must be first option"
    echo "  -h, --help                   Show this help, exit"
    echo "  -v, --version                Show current version, exit"
    echo "      --check                  Check if all dependencies are installed, exit"
    echo "  -c, --connect                Show connected imgur account, exit"
    echo "  -o, --open <true|false>      Override 'open' config"
    echo "  -e, --edit <true|false>      Override 'edit' config"
    echo "  -i, --edit-command <command> Override 'edit_command' config (include '%img'), sets --edit 'true'"
    echo "  -l, --login <true|false>     Override 'login' config"
    echo "  -a, --album <album_title>    Create new album and upload there"
    echo "  -A, --album-id <album_id>    Override 'album_id' config"
    echo "  -k, --keep-file <true|false> Override 'keep_file' config"
    echo "  -d, --auto-delete <s>        Automatically delete image after <s> seconds"
    echo "  -u, --update                 Check for updates, exit"
    echo "  file                         Upload file instead of taking a screenshot"
    exit 0;;
  -v | --version)
    echo "${current_version}"
    exit 0;;
  -s | --select)
    mode="select"
    shift;;
  -w | --window)
    mode="window"
    shift;;
  -f | --full)
    mode="full"
    shift;;
  -o | --open)
    # shellcheck disable=SC2034
    open="${2}"
    shift 2;;
  -e | --edit)
    edit="${2}"
    shift 2;;
  -i | --edit-command)
    edit_command="${2}"
    edit="true"
    shift 2;;
  -l | --login)
    login="${2}"
    shift 2;;
  -c | --connect)
    load_access_token
    fetch_account_info
    exit 0;;
  -a | --album)
    album_title="${2}"
    shift 2;;
  -A | --album-id)
    album_id="${2}"
    shift 2;;
  -k | --keep-file)
    keep_file="${2}"
    shift 2;;
  -d | --auto-delete)
    auto_delete="${2}"
    shift 2;;
  -u | --update)
    check_for_update
    exit 0;;
  *)
    upload_files=("${@}")
    break;;
  esac
done

if [ "${login}" = "true" ]; then
  # load before changing directory
  load_access_token
fi


if [ -n "${album_title}" ]; then
  if [ "${login}" = "true" ]; then
    response="$(curl -fsSL --stderr - \
      -F "title=${album_title}" \
      -H "Authorization: Bearer ${access_token}" \
      https://api.imgur.com/3/album)"
  else
    response="$(curl -fsSL --stderr - \
      -F "title=${album_title}" \
      -H "Authorization: Client-ID ${imgur_anon_id}" \
      https://api.imgur.com/3/album)"
  fi
  if grep -Eq '"success":\s*true' <<<"${response}"; then # Album creation successful
    echo "Album '${album_title}' successfully created"
    album_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
    del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
    handle_album_creation_success "https://imgur.com/a/${album_id}" "${del_id}" "${album_title}"

    if [ "${login}" = "false" ]; then
      album_id="${del_id}"
    fi
  else # Album creation failed
    err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
    test -z "${err_msg}" && err_msg="${response}"
    handle_album_creation_error "${err_msg}" "${album_title}"
  fi
fi

if [ -z "${upload_files[*]}" ]; then
  upload_files[0]=""
fi

for upload_file in "${upload_files[@]}"; do

  if [ -z "${upload_file}" ]; then
    cd "${file_dir}" || exit 1

    # new filename with date
    img_file="$(date +"${file_name_format}")"
    take_screenshot "${img_file}"
  else
    # upload file instead of screenshot
    img_file="${upload_file}"
  fi

  # get full path
  #cd "$(dirname "$(realpath "${img_file}")")"
  #img_file="$(realpath "${img_file}")"

  # check if file exists
  if ! [ -f "${img_file}" ]; then
    echo "file '${img_file}' doesn't exist !"
    read -r _
    exit 1
  fi

  # open image in editor if configured
  if [ "${edit}" = "true" ]; then
    edit_cmd=${edit_command//\%img/${img_file}}
    echo "Opening editor '${edit_cmd}'"
    if ! (eval "${edit_cmd}"); then
      echo "Error for image '${img_file}': command '${edit_cmd}' failed, not uploading. For more information visit https://github.com/jomo/imgur-screenshot/wiki/Troubleshooting" | tee -a "${log_file}"
      notify error "Something went wrong :(" "Information has been logged"
      exit 1
    fi
  fi

  if [ "${login}" = "true" ]; then
    upload_authenticated_image "${img_file}"
  else
    upload_anonymous_image "${img_file}"
  fi

  # delete file if configured
  if [ "${keep_file}" = "false" ] && [ -z "${1}" ]; then
    echo "Deleting temp file ${file_dir}/${img_file}"
    rm -rf "${img_file}"
  fi

  echo ""
done


if [ "${check_update}" = "true" ]; then
  check_for_update
fi

read -r _