#!/usr/bin/bash

git status >/dev/null || (echo "The current directory does not seem to be valid git repo" && exit 1)
type resize &>/dev/null || (echo "Missing 'resize' dependency" && exit 1)
type whiptail &>/dev/null || (echo "Missing 'whiptail' dependency" && exit 1)

eval `resize`
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

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" \
      "Remote" "Delete $USER/* upstream branches not present locally" \
      "Upstream" "Delete branches without origin/ or $USER/ upstream counterparts" \
      "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 master 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
      ;;

    Upstream)
      # preselect branches without origin or $USER upstream
      for b in $(git branch -vv | grep '\[.*\]' | grep -vE '\[(origin|lzap)/' | 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 master 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 "$last_commit_msg" | wc -l)" -eq 1 ]]; then
          [ "x$b" != "x$ACTIVE_BRANCH" ] && STATE[$b]=1
        fi
      done

      delete_action_selection
      delete_action action_local
      ;;

    Remote)
      # determine remote name
      REMOTE=$(whiptail --title "The git xcleaner - remote name" \
        --inputbox "Remote name:" $HEIGHT $WIDTH $USER 3>&1 1>&2 2>&3)
      git checkout $BASE || exit 1

      # relocate branch names as remotes
      declare -A STATE; STATE=()
      for b in $(git branch -a | sed 's/^\s*//' | sed 's/^remotes\///' | grep "^$REMOTE/" | sed "s/^$REMOTE\\///" | sort); do
        echo $b
        [ "x$b" != "x$ACTIVE_BRANCH" ] && STATE[$b]=1
      done

      # preselect those not present locally
      for b in $(git branch | sed -E 's/^\*?\s*//' | sort); do
        if [ ${STATE["$REMOTE/$b"]+exists} ]; then
          STATE["$REMOTE/$b"]=0
        fi
      done

      delete_action_selection
      delete_action action_remote
      ;;

    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
