#!/bin/bash

if [ -n "$APPSH_REPO" ]
then
  repo="$APPSH_REPO"
else
  repo="http://repo1.maven.org"
fi
 
# TODO: support file:// repositories
# TODO: look in the local repository first
# TODO: assert that we got a 200 OK
get() { 
  local exit

  curl -o $2 $1 -D curl.tmp

  exit=`grep "^HTTP/[0-9]\.[0-9] 200 .*" curl.tmp >/dev/null; echo $?`
  head=`head -n 1 curl.tmp`
  rm curl.tmp
  if [ "$exit" != 0 ]
  then
    echo "Unable to download $1: $head"
    exit 1
  fi
}

resolved_version=""
resolve_snapshot() {
  local base_url
  local metadata
  local zip_file=$1

  echo "Resolving version $version..."
  metadata=$BASEDIR/.app/var/download/$groupId-$artifactId-$version-metadata.xml
  base_url=$repo/$(echo $groupId | sed "s,\.,/,g")/$artifactId/$version
  get $base_url/maven-metadata.xml $metadata
  resolved_version=`xmlstarlet sel -t -m '//snapshotVersion[extension[text()="zip"]]' -v value $metadata`

  if [ -z "$resolved_version" ]
  then
    echo "Unable to resolve version."
    exit 1
  fi
  echo "Resolved version $version to $resolved_version"

  if [ -r $zip_file ]
  then
    echo "Artifact already downloaded."
    return 0
  fi
  echo "Downloading artifact"
  base_url=$repo/$(echo $groupId | sed "s,\.,/,g")/$artifactId/$version
  get $base_url/$artifactId-$resolved_version.zip $zip_file

  # TODO: download checksum. bash is too shady to trust
}

method_install_usage() {
  if [ -n "$1" ]
  then
    echo "Error:" "$@" >&2
  fi

  echo "usage: install <-r resolver> -u <url>" >&2
  echo "" >&2
  echo "Install package from a Maven repository:" >&2
  echo "  $0 [-n name] [-i instance] app install -r maven -u groupId:artifactId:version" >&2
  echo "" >&2
  echo "Install package from a file:" >&2
  echo "  $0 [-n name] [-i instance] app install -r file -u file [-v version]" >&2
  echo "The version defaults to the current timestamp" >&2
  exit 1
}

method_install() {
  local name="$1"; shift
  local instance="$1"; shift
  local version
  local resolver
  local url
  local groupId
  local artifactId
  local zip_file


  if [ $# -eq 0 ]
  then
    method_install_usage
  fi

  while getopts "n:i:v:r:u:" opt
  do
    case $opt in
      n)
        name=$OPTARG
        ;;
      i)
        instance=$OPTARG
        ;;
      v)
        version=$OPTARG
        ;;
      r)
        resolver=$OPTARG
        ;;
      u)
        url=$OPTARG
        ;;
      \?)
        method_install_usage "Invalid option: -$OPTARG" 
        ;;
    esac
  done

  if [ -z "$name" ]
  then
    method_install_usage "Missing required argument: -i name."
  fi

  if [ -z "$instance" ]
  then
    method_install_usage "Missing required argument: -i instance."
  fi

  if [ -z "$resolver" ]
  then
    method_install_usage "Missing required option: -r resolver"
  fi

  if [ -z "$url" ]
  then
    method_install_usage "Missing required option: -u url"
  fi

  case "$resolver" in
    maven)
      url=`echo $url | tr ":" " "`; set -- $url
      groupId=$1
      artifactId=$2
      version=$3

      if [ -z "$groupId" -o -z "$artifactId" -o -z "$version" ]
      then
        method_install_usage "Invalid Maven url."
      fi

      resolve_snapshot

      zip_file=$BASEDIR/.app/var/download/$groupId-$artifactId-$resolved_version.zip

      download_artifact $zip_file
      ;;
    file)
      if [ ! -r "$url" ]
      then
        echo "Could not read file: $url" >&2
        exit 1
      fi

      # TODO: should the zip file be copied into download/ so that
      # there's always a local copy?
      zip_file=$url

      if [ -z "$version" ]
      then
        version=`TZ=UTC date +"%Y%m%d-%H%M%S"`
      fi

      resolved_version=$version
      ;;
    *)
      method_install_usage "Invalid resolver type: $resolver" 
      ;;
  esac

  if [ -d $name/$instance/versions/$resolved_version ]
  then
    echo "Version $resolved_version is already installed"
    exit 1
  fi

  if [ ! -d $name/$instance ]
  then
    echo "Creating instance '$instance' for '$name'"
    mkdir -p $name/$instance
  fi

  mkdir -p $name/$instance/versions/$resolved_version

  echo "Unpacking..."
  unzip -q -d $name/$instance/versions/$resolved_version $zip_file

  if [ ! -d $BASEDIR/$name/$instance/versions/$resolved_version/root ]
  then
    echo "Invalid zip file, did not contain a ./root directory." >&2
    exit 1
  fi

  (
    cd $name/$instance/versions/$resolved_version
    if [ -d scripts ]
    then
      find scripts | xargs chmod +x
    fi

    if [ -x scripts/postinstall ]
    then
      echo "Running postinstall..."
      set +e
      env -i \
        PATH=/bin:/usr/bin \
        scripts/postinstall
      set -e
      ret=`echo $?`
      if [ "$ret" != 0 ]
      then
        echo "Postinstall failed!"
        exit 1
      fi
      echo "Postinstall completed successfully"
    fi
  )

  echo "Changing current symlink"
  rm -f $BASEDIR/$name/$instance/current
  ln -s versions/$resolved_version/root $BASEDIR/$name/$instance/current

  if [ -d $name/$instance/current/bin ]
  then
    (
      cd $name/$instance/current
      find bin -type f | xargs chmod +x
    )
  fi

  if [ -r $BASEDIR/.app/var/list ]
  then
    sed "/^$name:$instance/d" $BASEDIR/.app/var/list > $BASEDIR/.app/var/list.new
  fi
  echo "$name:$instance:$version:$url" >> $BASEDIR/.app/var/list.new
  mv $BASEDIR/.app/var/list.new $BASEDIR/.app/var/list
}

method_set_current_usage() {
  if [ -n "$1" ]
  then
    echo "Error:" "$@" >&2
  fi

  echo "usage: set-current -v version" >&2
  exit 1
}

method_set_current() {
  local name="$1"; shift
  local instance="$1"; shift
  local version

  if [ $# -eq 0 ]
  then
    method_set_current_usage
  fi

  while getopts "n:i:v:" opt
  do
    case $opt in
      n)
        name=$OPTARG
        ;;
      i)
        instance=$OPTARG
        ;;
      v)
        version=$OPTARG
        ;;
      \?)
        method_set_current_usage "Invalid option: -$OPTARG" 
        ;;
    esac
  done

  if [ -z "$version" ]
  then
    echo "Missing required option -v version." >&2
    exit 1
  fi

  assert_is_instance method_set_current_usage "$name" "$instance" "no"

  if [ ! -d $BASEDIR/$name/$instance/versions/$version ]
  then
    echo "Invalid version: $version."
    exit 1
  fi

  rm -f $BASEDIR/$name/$instance/current
  ln -s versions/$version/root $BASEDIR/$name/$instance/current

  return 0
}

method_list_usage() {
  if [ -n "$1" ]
  then
    echo "Error:" "$@" >&2
  fi

  echo "usage: list [-n name] [-P field]" >&2
  echo ""
  echo "List all installed applications:" >&2
  echo "  list" >&2
  echo ""
  echo "List all applications with the selected fields with parseable output:" >&2
  echo "  list -P instance -P version -n foo" >&2
  exit 1
}

method_list() {
  local filter_name="$1"; shift
  local instance="$1"; shift
  local mode="pretty"
  local vars
  local filter_name

  while getopts "P:n:" opt
  do
    case $opt in
      P)
        mode="parseable"
        vars="$vars $OPTARG"
        ;;
      n)
        filter_name=$OPTARG
        ;;
      \?)
        method_list_usage "Invalid option: -$OPTARG" 
        ;;
    esac
  done
 
  if [ ! -r $BASEDIR/.app/var/list ]
  then
    return
  fi

  if [ $mode = "pretty" ]
  then
    printf "%-20s %-20s %-20s\n" "Name" "Instance" "Version"
    list_apps "$filter_name" name instance version | (IFS=:; while read name instance version
    do
      printf "%-20s %-20s %-20s\n" "$name" "$instance" "$version"
    done)
  else
    list_apps "$filter_name" $vars
  fi
}

method_list_versions_usage() {
  if [ -n "$1" ]
  then
    echo "Error:" "$@" >&2
  fi

  echo "usage: list-versions -n name -i instance [-P]" >&2
  exit 1
}

method_list_versions() {
  local filter_name="$1"; shift
  local instance="$1"; shift
  local version
  local mode="pretty"

  if [ $# -eq 0 ]
  then
    method_list_versions_usage
  fi

  while getopts "n:i:P" opt
  do
    case $opt in
      n)
        name=$OPTARG
        ;;
      i)
        instance=$OPTARG
        ;;
      v)
        version=$OPTARG
        ;;
      P)
        mode="parseable"
        ;;
      \?)
        method_list_versions_usage "Invalid option: -$OPTARG" 
        ;;
    esac
  done

  assert_is_instance method_list_versions_usage "$name" "$instance" "no"

  if [ $mode = "pretty" ]
  then
    echo "Available versions for $name/$instance:"
  fi

  find_versions $name $instance

  return 0
}

method_app_usage() {
  if [ -n "$1" ]
  then
    echo "Error:" $@ >&2
  fi

  echo "usage: $0 app <method>" >&2
  echo "" >&2
  echo "Available methods:" >&2
  echo "  install       - Installs an application" >&2
  echo "  list          - List all installed applications" >&2
  echo "  list-versions - List all available versions for a single application" >&2
  echo "  set-current   - Set the current version" >&2
}

method_app() {
  local name="$1"; shift
  local instance="$1"; shift
  local method="$1"

  if [ $# -gt 0 ]
  then
    shift
  fi

  case "$method" in
    install)       method_install       "$name" "$instance" "$@" ;;
    list)          method_list          "$name" "$instance" "$@" ;;
    list-versions) method_list_versions "$name" "$instance" "$@" ;;
    set-current)   method_set_current   "$name" "$instance" "$@" ;;
    *)
      if [ -z "$method" ]
      then
        method_app_usage
      else
        method_app_usage "Unknown method $method"
      fi
      ;;
  esac
  exit $?
}