#!/usr/bin/env bash
#==========================================================================
#
#   Copyright NumFOCUS
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#          http://www.apache.org/licenses/LICENSE-2.0.txt
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
#==========================================================================*/

# Utility functions
print_help() {
cat << EOF
Usage: $0 [--folder-id <folder_id>] <binary/file/path/in/repo/1> [<binary/file/path/in/repo/2> ...]


Use this script to upload binary testing data for SimpleITK and related projects.

Binary data, e.g. test input images, are not stored in Git because they
will bloat a Git repository's size.


To use the script:

1. Sign up for an account at https://data.kitware.com
2. Place the binary file at the desired location in the Git repository.
3. Run this script, and pass in the binary file(s) as arguments to script.
4. In test/CMakeLists.txt, use the sitk_add_test macro and reference the file
   path with \`DATA\` and braces, e.g.: DATA{<Relative/Path/To/Source/Tree/File>}.
5. Re-build SimpleITK, and the testing data will be downloaded into the build tree.


If the Git `girder.api-key` config or `GIRDER_API_KEY` environmental variable
is not set, a prompt will appear for your username and password. The API key
can be created in the data.kitware.com user account web browser interface. The
 Git config can be set by running:
  `git config girder.api-key YOUR_GIRDER_API_KEY`


The script will authenticate to data.kitware.com, upload the file to your
user account's Public folder, and create a *.sha512 CMake ExternalData
content link file. To specify a different folder, use the --folder flag.
After the content link has been created, add the *.sha512 file to your
git commit. The binary file will be removed from the source tree following
upload.
EOF
}

die() {
  echo "$@" 1>&2; exit 1
}

json_field() {
  local key=$1
  local json=$2
  echo $json | awk 'BEGIN { FS="\""; RS="," }; { if ($2 == "'$key'") {print $4} }'
}


# Parse arguments
help=false
folder_id=""
while test $# -gt 0;
do
    opt="$1";
    case "$opt" in
        "-h"|"--help")
          shift;
          help=true
          break;;
        "-f"|"--folder-id")
          shift;
          folder_id="$1"
          shift;;
        *)
          break;;
   esac
done
binary_files="$@"

if test "${binary_files}" = "" || $help; then
  print_help
  exit 1
fi


# Check for required dependencies
if ! type curl > /dev/null; then
  die "Please install the curl executable."
fi
if ! type wc > /dev/null; then
  die "Please install the wc executable."
fi


# Authenticate
token=""
git_config_api_key=$(git config --get girder.api-key || echo '')
if test -n "${git_config_api_key}"; then
  token_response=$(curl -s -X POST --header 'Content-Length: 0' --header 'Content-Type: application/json' --header 'Accept: application/json' "https://data.kitware.com/api/v1/api_key/token?key=${git_config_api_key}&duration=1" || die "Could not retrieve token from API key.")
  token=$(json_field "token" "${token_response}")
fi
if test -z "${token}" -a -n "${GIRDER_API_KEY}"; then
  token_response=$(curl -s -X POST --header 'Content-Length: 0' --header 'Content-Type: application/json' --header 'Accept: application/json' "https://data.kitware.com/api/v1/api_key/token?key=${GIRDER_API_KEY}&duration=1" || die "Could not retrieve token from API key.")
  token=$(json_field "token" "${token_response}")
fi
if test -z "${token}"; then
  if ! type base64 > /dev/null; then
    die "Please install the base64 executable."
  fi
  echo "Please provide your"
  echo ""
  echo "    https://data.kitware.com"
  echo ""
  read -p "username: " username
  read -p "password: " -s password
  basic_content=$(echo -n "${username}:${password}" | base64)
  token_response=$(curl -s -X GET --header "Girder-Authorization: Basic ${basic_content}" --header 'Accept: */*' --header 'Host: data.kitware.com' --header 'Referer: https://data.kitware.com' 'https://data.kitware.com/api/v1/user/authentication' || die "Could not retrieve token from username / password.")
  token=$(json_field "token" "${token_response}")
fi
if test -z "${token}"; then
  die "Could not authenticate to https://data.kitware.com"
fi


# Get user / folder
user_id_response=$(curl -s -X GET --header 'Accept: application/json' --header "Girder-Token: ${token}" 'https://data.kitware.com/api/v1/user/me' || die 'Could not get user id.')
user_id=$(json_field "_id" "${user_id_response}")

if test -z "$folder_id"; then
  folder_id_response=$(curl -s -X GET --header 'Accept: application/json' --header "Girder-Token: ${token}" "https://data.kitware.com/api/v1/folder?parentType=user&parentId=${user_id}&name=Public&limit=3&sort=lowerName&sortdir=1" || die 'Could not get folder id.')
  folder_id=$(json_field "_id" "${folder_id_response}")
fi


# Upload files and create content links
generated_content_links=""
md5_content_link_conflicts=""
for binary_file in $binary_files; do
  if test ! -e $binary_file; then
    die "$binary_file does not exist."
  fi
  item_name=$(basename "$binary_file")

  create_item_response=$(curl -s -X POST --header 'Content-Length: 0' --header 'Content-Type: application/x-www-form-urlencoded' --header 'Accept: application/json' --header "Girder-Token: ${token}" "https://data.kitware.com/api/v1/item?folderId=${folder_id}&name=${item_name}&description=SimpleITK%20testing%20data%20uploaded%20by%20SimpleITK%2FUtilities%2FUploadBinaryData.sh&reuseExisting=true" || die 'Could not create item.')
  item_id=$(json_field "_id" "${create_item_response}")

  size=$(wc -c < "$binary_file" | sed 's/^[[:space:]]*//')
  echo "Uploading ${item_name}..."
  upload_file_response=$(curl -X POST --data-binary "@${binary_file}" --header 'Content-Type: application/json' --header 'Accept: application/json' --header "Girder-Token: ${token}" "https://data.kitware.com/api/v1/file?parentType=item&parentId=${item_id}&name=${item_name}&size=${size}" || die 'Could not upload file.')
  file_id=$(json_field "_id" "${upload_file_response}")

  curl -s -X GET --output "${binary_file}.sha512" --header 'Accept: text/plain' --header "Girder-Token: ${token}" "https://data.kitware.com/api/v1/file/${file_id}/hashsum_file/sha512" || die 'Could not get file sha512sum.'
  generated_content_links="$generated_content_links ${binary_file}.sha512"
  if type cmake > /dev/null; then
    cmake_sha512sum=$(cmake -E sha512sum "$binary_file" 2> /dev/null)
    # If sufficient CMake version, ...
    if test $? -eq 0; then
      local_sha512=$(echo $cmake_sha512sum | awk '{print $1}')
      remote_sha512=$(cat "${binary_file}.sha512")
      if test "$local_sha512" != "$remote_sha512"; then
        die "Local file hash does not match uploaded file hash."
      fi
    fi
  fi

  md5_content_link="${binary_file}.md5"
  if test -e "$md5_content_link"; then
    md5_content_link_conflicts="$md5_content_link_conflicts $md5_content_link"
  fi
  rm $binary_file
done


# Recommend next steps
cat << EOF

Testing data upload complete.

Now run:

  git add --$generated_content_links

EOF
if test -n "$md5_content_link_conflicts"; then
cat << EOF
and:

  git rm --$md5_content_link_conflicts
EOF
fi
