#!/usr/bin/bash
# vim: ts=2:sw=2:et
git status >/dev/null || (echo "The current directory does not seem to be valid git repo" && exit 1)
type whiptail &>/dev/null || (echo "Missing 'whiptail' dependency" && exit 1)

DEFBRANCH=main
BORDER=5
WIDTH=$((COLUMNS - BORDER))
HEIGHT=$((LINES - BORDER))
WHEIGHT=$((LINES - BORDER - 8))
WIDTH_MENU=78
HEIGHT_MENU=18
WHEIGHT_MENU=$((HEIGHT_MENU - 8))
SMALL_WIDTH=60
SMALL_HEIGHT=8

if git show-ref --quiet refs/heads/master; then
  DEFBRANCH=master
fi

delete_action_selection() {
  # construct the checkbox command
  CHECKBOX_CMD=""
  for b in $BRANCH_LIST; do
    [[ -z "${STATE[$b]}" ]] && STATE[$b]=0
    CHECKBOX_CMD+="$b ${STATE[$b]} "
  done

  # open the checkbox list
  TO_BE_DELETED=()
  for b in $(whiptail --title "The git xcleaner - branch selection" \
    --separate-output --noitem \
    --checklist "Select branches to be deleted and be careful:" \
    $HEIGHT $WIDTH $WHEIGHT $CHECKBOX_CMD 3>&1 1>&2 2>&3); do
    TO_BE_DELETED+=($b)
  done

  CONFIRMED=0
  if [ ${#TO_BE_DELETED[@]} -ne 0 ]; then
    # explicitly confirm the action
    if (whiptail --title "The git xcleaner - delete confirmation" \
      --yesno "Are you sure to delete selected branches (${#TO_BE_DELETED[@]})?" $SMALL_HEIGHT $SMALL_WIDTH); then
      CONFIRMED=1
    fi
  fi
}

delete_action() {
  if [ $CONFIRMED -eq 1 ]; then
    PERCENT=$((100 / ${#TO_BE_DELETED[@]}))
    GAUGE=0
    {
      for b in "${TO_BE_DELETED[@]}"; do
        eval $1 1>&2
        echo $GAUGE
        GAUGE=$((GAUGE + PERCENT))
      done
      echo 100
    } | whiptail --title "The git xcleaner - working" \
      --gauge "Deleting selected branches..." $SMALL_HEIGHT $WIDTH 0
  fi
}

action_local() {
  git branch -D $b &>>"$HOME/.git-xcleaner.log"
}

action_remote() {
  git push $REMOTE :${b/$REMOTE\//} &>>"$HOME/.git-xcleaner.log"
}

while true; do

  ACTIVE_BRANCH=$(git rev-parse --abbrev-ref HEAD)

  # hash with preselect state (0/1)
  BRANCH_LIST=$(git branch | sed -E 's/^\*?\s*//' | sort)
  declare -A STATE
  STATE=()

  # load all branch names
  for b in $BRANCH_LIST; do
    STATE[$b]=0
  done

  ACTION=$(whiptail --title "The git xcleaner - main menu" --cancel-button "Help" --default-item "Exit" \
    --menu "Select action. This is safe, review on the next screen." $HEIGHT_MENU $WIDTH_MENU $WHEIGHT_MENU \
    "Merged" "Delete merged branches (--merged)" \
    "Messages" "Delete (rebased) branches with the same commit messages" \
    "Pruned" "Delete pruned branches (deleted tracking branches)" \
    "Manual" "Interactive manual deletion" \
    "Exit" "Return to shell" \
    "Help" "Show help" \
    3>&1 1>&2 2>&3)
  #"Prune" "Delete stale remote-tracking branches" \

  case $ACTION in
  Manual)
    delete_action_selection
    delete_action action_local
    ;;

  Merged)
    # checkout base branch
    BASE=$(whiptail --title "The git xcleaner - branch to search" \
      --inputbox "Base branch:" $HEIGHT $WIDTH $DEFBRANCH 3>&1 1>&2 2>&3)

    # preselect merged branch names
    for b in $(git branch --merged "$BASE" | sed -E 's/^\*?\s*//' | sort); do
      [ "x$b" != "x$BASE" ] && STATE[$b]=1
    done

    delete_action_selection
    delete_action action_local
    ;;

  Pruned)
    # preselect pruned branches (must do git fetch --prune prior this)
    for b in $(git for-each-ref --format '%(refname:short) %(upstream:track)' refs/heads | grep '\[gone\]' | awk '{ print $1 }' | sort); do
      [ "x$b" != "x$ACTIVE_BRANCH" ] && STATE[$b]=1
    done

    delete_action_selection
    delete_action action_local
    ;;

  Messages)
    # checkout base branch
    BASE=$(whiptail --title "The git xcleaner - branch to search" \
      --inputbox "Base branch:" $HEIGHT $WIDTH $DEFBRANCH 3>&1 1>&2 2>&3)
    git checkout "$BASE" &>/dev/null || exit 1

    # search commit messages and preselect branches
    for b in "${!STATE[@]}"; do
      last_commit_msg="$(git log --oneline --format=%f -1 $b)"
      if [[ "$(git log --oneline --format=%f | grep -c "$last_commit_msg")" -eq 1 ]]; then
        [ "x$b" != "x$ACTIVE_BRANCH" ] && STATE[$b]=1
      fi
    done

    delete_action_selection
    delete_action action_local
    ;;

  Exit)
    exit 0
    ;;
  *)
    HELP_TEXT=/usr/share/doc/git-xcleaner/git-xcleaner.1.txt
    if [ -f $HELP_TEXT ]; then
      whiptail --title "The git xcleaner - help" --textbox $HELP_TEXT $HEIGHT $WIDTH
    else
      whiptail --title "The git xcleaner - help" --infobox "Help not available - use packaged version" $HEIGHT $WIDTH
    fi
    ;;
  esac
done
