#!/bin/bash
# Copyright 2019 The Nomulus Authors. All Rights Reserved.
#
# 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
#
# 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.
#
# This script applies Google Java format to modified regions in Java source
# files in a Git repository. It assumes that the repository has a 'master'
# branch that is only used for merging and is never directly worked on.
#
# If invoked on the master branch, this script will format the modified lines
# relative to HEAD. Otherwise, it uses the
# 'git merge-base --fork-point origin/master' command to find the latest
# fork point from origin/master, and formats the modified lines between
# the fork point and the HEAD of the current branch.
#
# Background: existing code base does not conform to Google Java format. Since
# the team wants to keep the 'blame' feature (find last modifier of a line)
# usable, we do not want to reformat existing code.

set -e

USAGE="
$(basename "$0") [--help] check|format|show
Incrementally format modified java lines in Git.

where:
    --help  show this help text
    check  check if formatting is necessary
    format format files in place
    show   show the effect of the formatting as unified diff"

SCRIPT_DIR="$(realpath $(dirname $0))"
JAR_NAME="google-java-format-1.8-all-deps.jar"

# Locate the java binary.
if [ -n "$JAVA_HOME" ]; then
  JAVA_BIN="$JAVA_HOME/bin/java"
  if [ ! -x "$JAVA_BIN" ]; then
    echo "No java binary found in JAVA_HOME (JAVA_HOME is $JAVA_HOME)"
    exit 1
  fi
else
  # Use java from the path.
  JAVA_BIN="$(which java)" || JAVA_BIN=""
  if [ -z "$JAVA_BIN" ]; then
    echo "JAVA_HOME is not defined and java was not found on the path"
    exit 1
  fi
fi

if ! "$JAVA_BIN" -version 2>&1 | grep 'version "11\.' >/dev/null; then
  echo "Bad java version.  Requires java 11, got:"
  "$JAVA_BIN" -version
  exit 1
fi

function runGoogleJavaFormatAgainstDiffs() {
  local forkPoint="$1"
  shift

  git diff -U0 "$forkPoint" | \
      ${SCRIPT_DIR}/google-java-format-diff.py \
      --java-binary "$JAVA_BIN" \
      --google-java-format-jar "${SCRIPT_DIR}/${JAR_NAME}" \
      -p1 "$@" | tee gjf.out
}

# Show the file names in a diff preceeded by a message.
function showFileNames() {
  local message="$1"

  awk -v "message=$message" '/\+\+\+ ([^ ]*)/ { print message $2 }' 1>&2
}

function showNoncompliantFiles() {
  local forkPoint="$1"
  local message="$2"

  runGoogleJavaFormatAgainstDiffs "$forkPoint" | showFileNames "$2"
}

function callGoogleJavaFormatDiff() {
  local forkPoint
  forkPoint=$(git merge-base origin/master HEAD)

  local callResult
  case "$1" in
    "check")
      local output=$(runGoogleJavaFormatAgainstDiffs "$forkPoint")
      echo "$output" | showFileNames "\033[1mNeeds formatting: "
      callResult=$(echo -n "$output" | wc -l)
      ;;
    "format")
      # Unfortunately we have to do this twice if we want to see the names of
      # the files that got reformatted
      showNoncompliantFiles "$forkPoint" "\033[1mReformatting: "
      callResult=$(runGoogleJavaFormatAgainstDiffs "$forkPoint" -i)
      ;;
    "show")
      callResult=$(runGoogleJavaFormatAgainstDiffs "$forkPoint")
      ;;
  esac
  echo -e "\033[0m" 1>&2
  echo "${callResult}"
  exit 0
}

function isJavaFormatNeededOnDiffs() {
  local modifiedLineCount
  modifiedLineCount=$(callGoogleJavaFormatDiff "check")

  if [[ ${modifiedLineCount} -ne 0 ]]; then
    echo "true"
  else
    echo "false"
  fi
  exit 0
}

# The main function of this script:
if [[ $# -eq 1 && $1 == "check" ]]; then
  isJavaFormatNeededOnDiffs
elif [[ $# -eq 1 && $1 == "format" ]]; then
  callGoogleJavaFormatDiff "format"
elif [[ $# -eq 1 && $1 == "show" ]]; then
  callGoogleJavaFormatDiff "show"
else
  echo "${USAGE}"
fi