#!/bin/bash

usage() {
  cat <<EOF
Usage:
  ${0}                  Remerge all files with conflict markers in the git working tree
  ${0} [FILE...]        Remerge the given files

Options:
  -t, --tool {bcompare,meld,vimdiff}
    Use the specified merge tool.
EOF
}

# shellcheck disable=SC2155
readonly BIN_DIR=$(dirname "${BASH_SOURCE[0]}")
readonly SPLIT3="${BIN_DIR}/split3.awk"
readonly CONFLICT_MARKER_BEGIN='^<{7}( .+)?$'
readonly CONFLICT_MARKER_BASE='^\|{7}( .+)?$'

TEMP_FILES=()
cleanup() {
  rm -rf "${TEMP_FILES[@]}"
}
trap cleanup EXIT

xtrace() {
  (
    set -x
    "${@}"
  )
}

mergetool() {
  local file="${1}"
  local MERGED="$file"
  local BASE="${file}:BASE"
  local LOCAL="${file}:LOCAL"
  local REMOTE="${file}:REMOTE"
  TEMP_FILES+=("$BASE" "$LOCAL" "$REMOTE")

  local has_base=false
  if grep -qE "${CONFLICT_MARKER_BASE}" "$file"; then
    has_base=true
  fi

  $has_base && awk -f "$SPLIT3" -v TARGET=BASE <"$file" >"$BASE"
  awk -f "$SPLIT3" -v TARGET=LOCAL <"$file" >"$LOCAL"
  awk -f "$SPLIT3" -v TARGET=REMOTE <"$file" >"$REMOTE"

  case "$MERGETOOL" in
    bc*)
      if $has_base; then
        xtrace bcompare "$LOCAL" "$REMOTE" "$BASE" -mergeoutput="$MERGED"
      else
        xtrace bcompare "$LOCAL" "$REMOTE" -mergeoutput="$MERGED"
      fi
      ;;
    meld)
      if $has_base; then
        xtrace meld "$LOCAL" "$BASE" "$REMOTE" -o "$MERGED"
      else
        xtrace meld "$LOCAL" "$MERGED" "$REMOTE"
      fi
      ;;
    vim*)
      if $has_base; then
        xtrace vimdiff -c '4wincmd w | wincmd J' "$LOCAL" "$BASE" "$REMOTE" "$MERGED"
      else
        xtrace vimdiff -c 'wincmd l' "$LOCAL" "$MERGED" "$REMOTE"
      fi
      ;;
  esac
}

#
# BEGIN
#

MERGETOOL=vimdiff
if [[ -n "$DISPLAY" ]]; then
  if command -v bcompare; then
    MERGETOOL=bcompare
  elif command -v meld; then
    MERGETOOL=meld
  fi
fi >/dev/null

while [[ "$1" =~ ^- ]]; do
  arg="${1}"
  shift
  case "$arg" in
    --) break ;;
    -t | --tool)
      MERGETOOL="${1}"
      shift
      ;;
    -h | --help | --usage)
      usage
      exit 0
      ;;
    *)
      usage
      exit 1
      ;;
  esac
done

TRUST_EXIT_CODE=false
if git rev-parse >/dev/null 2>/dev/null; then
  case "$MERGETOOL" in
    bc* | vim*)
      TRUST_EXIT_CODE=true
      ;;
  esac
fi

readonly MERGETOOL
readonly TRUST_EXIT_CODE

FILES_UNFILTERED=()
if [[ "${#}" -eq 0 ]]; then
  while IFS= read -r -d '' ARG; do
    FILES_UNFILTERED+=("$ARG")
  done < <(git -c grep.fallbackToNoIndex=true grep -zlE "$CONFLICT_MARKER_BEGIN" 2>/dev/null)
else
  FILES_UNFILTERED+=("${@}")
fi

FILES=()
for file in "${FILES_UNFILTERED[@]}"; do
  if ! [[ -f "$file" ]] || [[ -L "$file" ]]; then
    echo "[SKIPPED] ${file}: not a regular file"
  elif ! grep -qE "$CONFLICT_MARKER_BEGIN" "$file"; then
    echo "[SKIPPED] ${file}: no conflict markers found"
  else
    FILES+=("$file")
  fi
done

echo "Found files with conflict markers:"
printf '    %s\n' "${FILES[@]}"

for file in "${FILES[@]}"; do
  echo
  echo "Merging '${file}'"

  mergetool "$file"
  exit_code="$?"
  if [[ "$exit_code" -ne 0 ]]; then
    echo "Failed to merge '${file}'"
  fi

  if $TRUST_EXIT_CODE && [[ "$exit_code" -eq 0 ]]; then
    if [[ -n "$(git ls-files "$file")" ]]; then
      xtrace git add "$file"
    fi
    continue
  fi
  read -r -p "Continue merging other files? [Y/n]" -n 1 yn || exit
  echo
  [[ "$yn" == [nNqQ] ]] && exit "$exit_code"
done

exit 0
