#!/usr/bin/env bash
set -e
VERSION=0.1
LOGIN_PAGE=https://www.humblebundle.com/login
HOME_PAGE=https://www.humblebundle.com/home
COOKIE_JAR=
FILE=
DESTINATION=
DOWNLOAD=1
USERNAME=
PASSWORD=
KEYS=()
STORAGE=
login() {
  curl -s --cookie-jar "$COOKIE_JAR" \
    --data "username=$USERNAME" --data "password=$PASSWORD" "$LOGIN_PAGE"
}
discover_url() {
  local LISTING_PAGE="$1"
  curl -s --cookie "$COOKIE_JAR" "$LISTING_PAGE" | grep "/$FILE?" | \
    sed -e "s/.* data-web='\([^']*\)'.*/\1/" | head -n 1
}
usage() {
  echo "hib-dlagent $VERSION"
  echo "Tool to download Humble Indie Bundle binaries by file name"
  echo
  echo "Usage: $0 [OPTIONS] FILE"
  echo "Options:"
  echo " -d 
   Directory for searching and saving files. The normal save"
  echo "            location is still used, but will be a symbolic link"
  echo " -h         This help"
  echo " -k    Search key's files. Use multiple times for multiple keys"
  echo " -o   Name to use when saving file"
  echo " -p   Use pass to login. If specified multiple times, the last is"
  echo "            used"
  echo " -s         Print URL to stdout instead of downloading. Incompatible"
  echo "            with -d"
  echo " -u   Use user to login. Search account's files. If specified"
  echo "            multiple times, the last is used"
  echo
  echo "If you specify -u, then all of that account's bundles are searched. If"
  echo "a key is associated with a HIB account then you must use -u/-p, since"
  echo "that key only works when logged into that account. It is not helpful to"
  echo "specify -k for bundles associated with an account."
}
handle_download() {
  local SAVE_FILE="$1"
  local LISTING_PAGES=()
  COOKIE_JAR=$(mktemp)
  if [ -n "$USERNAME" ]; then
    login
    LISTING_PAGES+=("$HOME_PAGE")
  fi
  for KEY in "${KEYS[@]}"; do
    LISTING_PAGES+=("https://www.humblebundle.com/downloads?key=$KEY")
  done
  if [ -z "$LISTING_PAGES" ]; then
    echo "You must specify at least one of -u and -k" >&2
    exit 1
  fi
  local URL
  for LISTING_PAGE in "${LISTING_PAGES[@]}"; do
    URL=$(discover_url "$LISTING_PAGE")
    if [ -n "$URL" ]; then break; fi
  done
  # Cookie no longer necessary.
  rm "$COOKIE_JAR"
  if [ ! -n "$URL" ]; then
    echo "Could not find URL for file: $FILE" >&2
    exit 2
  fi
  if [ $DOWNLOAD -eq 0 ]; then
    echo "$URL"
  else
    curl -C - --retry 3 --retry-delay 3 -o "$SAVE_FILE" "$URL"
  fi
}
main() {
  if [ $# -eq 0 ]; then
    usage
    exit 1
  fi
  while getopts "hd:k:o:p:su:" opt; do
    case $opt in
      \?)
        exit 1
      ;;
      h)
        usage
        exit 1
      ;;
      d)
        STORAGE="$OPTARG"
      ;;
      k)
        KEYS+=("$OPTARG")
      ;;
      o)
        DESTINATION="$OPTARG"
      ;;
      p)
        PASSWORD="$OPTARG"
      ;;
      s)
        DOWNLOAD=0
      ;;
      u)
        USERNAME="$OPTARG"
      ;;
    esac
  done
  shift $(($OPTIND - 1))
  if [ $# == 0 ]; then
    echo "Missing argument FILE" >&2
    exit 1
  fi
  if [ $# != 1 ]; then
    echo "Unexpected argument: $2" >&2
    exit 1
  fi
  if [ $DOWNLOAD -eq 0 -a -n "$STORAGE" ]; then
    echo "-s and -d are incompatible" >&2
    exit 1
  fi
  FILE="$1"
  if [ "$FILE" != "$(basename "$FILE")" ]; then
    # We need a simple file name without any slashes. Instead of erroring, we
    # strip the file name out so that hib://filename is a valid argument.
    FILE=$(basename "$FILE")
    # Since we are doing something fancy, give the user a clue that we modified
    # their input.
    echo "Searching for $FILE" >&2
  fi
  if [ -z "$DESTINATION" ]; then
    DESTINATION="."
  fi
  if [ -d "$DESTINATION" ]; then
    DESTINATION="$DESTINATION/$FILE"
  fi
  if [ -z "$STORAGE" ]; then
    handle_download "$DESTINATION"
  else
    local STORAGE_FILE
    STORAGE_FILE=$(find "$STORAGE" -name "$FILE")
    if [ -z "$STORAGE_FILE" ]; then
      STORAGE_FILE="$STORAGE/$FILE"
      handle_download "$STORAGE_FILE"
    fi
    if [ ! "$STORAGE_FILE" -ef "$DESTINATION" ]; then
      ln -s "$STORAGE_FILE" "$DESTINATION"
      if [ ! "$STORAGE_FILE" -ef "$DESTINATION" ]; then
        # If DESTINATION is in a different directory than the CWD and STORAGE
        # is a relative path, then ln -s will produce a broken link. To produce
        # a valid link we resolve the path with readlink. This is unfortunate
        # because having relative links is nicer and we will undo any symlinks
        # in the path. Therefore, we detect when things have broken and only
        # use readlink when we must.
        STORAGE_FILE=$(readlink -f "$STORAGE_FILE")
        rm "$DESTINATION"
        ln -s "$STORAGE_FILE" "$DESTINATION"
      fi
    fi
  fi
}
main "$@"