#!/bin/bash
set -u

declare ShelvedCL=
declare -i ErrorCount=0
declare CfgDir=~/.config/vshelf
declare CfgFile="$CfgDir/vshelf.cfg"
declare -A Files

function msg () { echo -e "$*"; }
function errmsg () { msg "\nError: ${1:-Unknown Error}\n"; ErrorCount+=1; }
function bail () { errmsg "${1:-Unknown Error}"; exit "$ErrorCount"; }

function usage () {
    msg "\nUSAGE: vshelf [<PendingCL>]

	This script display diffs between current local workspace fiels in numbered
	pending changelist and shelved changelist of the same number.

	If no PendingCL is specified, the last PendingCL specified in the most recent
	prior run of this script is used."

	exit 2
}

#------------------------------------------------------------------------------
# Verify shelf. Display diffs between current local workspace fiels in numbered
# pending changelist and shelved changelist of the same number.
#
# Usage Example: vshelf 27800
#------------------------------------------------------------------------------
function vshelf () {
   local shelf="${1:-}"
   local -i diffCount=0
	local tmpFile=

   for df in $(p4 -ztag -F %depotFile% files @="$shelf"); do
      # Warning: If the client spec 'View:' field contains ditto mappings,
      # there will be more than one 'path' value returned by 'p4 where', with
      # one entry returned per line. We rather arbitrarily choose the first
      # returned path mapping (ala 'head -1'), hoping that's the correct one.
      # TO DO: If possible, find a reliable way to determine which is the
      # correct, or at least most likely to be correct, path mapping if ditto
      # mappings are used and 'p4 where' returns more than one value.
      path="$(p4 -ztag -F %path% where "$df" | head -1)"
		Files[$path]=1
      msg "Checking: $path"
      tmpFile="${path}.${shelf}.diff.tmp"
      rm -f "$tmpFile"
      p4 print -q -o "$tmpFile" "${path}@=${shelf}"

      if ! diff -q "${path}.$shelf.diff.tmp" "${path}"; then
         msg "Warning: Differences detected for: $path"
         diff "${path}.$shelf.diff.tmp" "${path}"
         diffCount+=1
      fi
      rm -f "$tmpFile"
   done

   while IFS= read -r df; do
      path="$(p4 -ztag -F %path% where "$df" | head -1)"
      if [[ -z "${Files[$path]:-}" ]]; then
         msg "Warning: File '$path' is in pending CL @$shelf but is not in the shelf."
         diffCount+=1
      fi
   done < <(p4 -ztag change -o "$shelf" | awk '/^\.\.\. Files[0-9]+/ {print substr($0, index($0, "//"))}')

   if [[ "$diffCount" -eq 0 ]]; then
      msg "\nNo diffs found.\n"
   else
      msg "\nWarning: found $diffCount diffs. Maybe force-reshelve or replace?\n"
   fi
}

# If no ShelvedCL was provided on the command line, see if we can extract the most recent
# ShelvedCL from config file.
if [[ -z "${1:-}" ]]; then
   if [[ -r "$CfgFile" ]]; then
      if [[ $(cat "$CfgFile") =~ ^[[:space:]]*ShelvedCL[[:space:]]*=[[:space:]]*(.*[^[:space:]]) ]]; then
         ShelvedCL="${BASH_REMATCH[1]}"
         [[ "$ShelvedCL" =~ ^[0-9]+$ ]] ||\
            bail "Malformed ShelvedCL found in config file: $CfgFile\nContents:$(cat "$CfgFile")\n"
      else
         bail "Could not extract ShelvedCL from config file: $CfgFile"
      fi
   else
      errmsg "No shelved CL provied and no previous shelves found."
      usage
   fi
elif [[ "$1" =~ ^[0-9]+$ ]]; then
   ShelvedCL="$1"
else
   usage
fi

vshelf "$ShelvedCL"
[[ -d "$CfgDir" ]] || mkdir -p "$CfgDir" || bail "Could not do: mkdir -p \"$CfgDir\""
echo "ShelvedCL=$ShelvedCL" > "$CfgFile" || errmsg "Could not update $CfgFile with ShelvedCL=$ShelvedCL"

exit "$ErrorCount"
