#!/usr/bin/env bash
#
# helper functions to obtain git related information

#######################################
# let user select a commit interactively
# credit to forgit for the git log format
# Arguments:
#   $1: the helper message to display in the fzf header
#   $2: files to show diff against HEAD
# Outputs:
#   the selected commit 6 char code
#   e.g. b60b330
#######################################
# TODO: line33 "${files[*]}" cannot handle space in file names, but "${files[@]}" won't get properly
# processed by git for whatever reason just in this preview situation but works every where else, HELP needed.
# although this won't affect any real functionality or break the code, it will just print a error message in preview.
function get_commit() {
  local header="${1:-select a commit}"
  local files=("${@:2}")
  if [[ "${#files[@]}" -eq 0 ]]; then
    git --git-dir="${DOTBARE_DIR}" --work-tree="${DOTBARE_TREE}" \
      log --color=always --format='%C(auto)%h%d %s %C(black)%C(bold)%cr' \
      | fzf --header="${header}" --no-multi \
          --preview "echo {} \
          | awk '{print \$1}' \
          | xargs -I __ git --git-dir=${DOTBARE_DIR} --work-tree=${DOTBARE_TREE} \
              show --color=always  __ \
          | ${DOTBARE_DIFF_PAGER}" \
      | awk '{print $1}'
  else
    git --git-dir="${DOTBARE_DIR}" --work-tree="${DOTBARE_TREE}" \
      log --oneline --color=always --decorate=short \
      | fzf --header="${header}" --no-multi --preview "echo {} \
          | awk '{print \$1}' \
          | xargs -I __ git --git-dir=${DOTBARE_DIR} --work-tree=${DOTBARE_TREE} \
              diff --color=always __ ${files[*]} \
          | ${DOTBARE_DIFF_PAGER}" \
      | awk '{print $1}'
  fi
}

#######################################
# let user select a branch interactively
# Arguments:
#   $1: the helper message to display in the fzf header
# Outputs:
#   the selected branch name
#   e.g. master
#######################################
function get_branch() {
  local header="${1:-select a branch}"
  git --git-dir="${DOTBARE_DIR}" --work-tree="${DOTBARE_TREE}" branch -a \
    | awk '{
        if ($0 ~ /\*.*\(HEAD.*/) {
          gsub(/\* /, "", $0)
          print "\033[32m" $0 "\033[0m"
        } else if (match($1, "\\*") != 0) {
          print "\033[32m" $2 "\033[0m"
        } else if ($0 ~ /^[ \t]+remotes\/.*/) {
          gsub(/[ \t]+/, "", $0)
          print "\033[31m" $0 "\033[0m"
        } else {
          gsub(/^[ \t]+/, "", $0)
          print $0
        }
      }' \
    | fzf --no-multi --header="${header}" \
        --preview="echo {} \
      | awk '{
          if (\$0 ~ /.*HEAD.*/) {
            print \"HEAD\"
          } else {
            print \$0
          }
        }' \
      | xargs -I __ git --git-dir=${DOTBARE_DIR} --work-tree=${DOTBARE_TREE} \
          log --color=always --color=always --format='%C(auto)%h%d %s %C(black)%C(bold)%cr' __" 
}

#######################################
# let user select a dotbare tracked file interactively
# Arguments:
#   $1: the helper message to display in the fzf header
#   $2: print option, values (full|raw)
#   $3: if exist, don't do multi selection, do single
# Outputs:
#   the selected file path
#   e.g.$HOME/.config/nvim/init.vim
#######################################
function get_git_file() {
  local mydir
  local header="${1:-select tracked file}"
  local print_opt="${2:-full}"
  mydir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
  set_fzf_multi "$3"
  git --git-dir="${DOTBARE_DIR}" --work-tree="${DOTBARE_TREE}" \
    ls-files --full-name --directory "${DOTBARE_TREE}" \
    | fzf --header="${header}" \
        --preview "${mydir}/../helper/preview.sh ${DOTBARE_TREE}/{}" \
    | awk -v home="${DOTBARE_TREE}" -v print_opt="${print_opt}" '{
        if (print_opt == "full") {
          print home "/" $0
        } else {
          print $0
        }
      }'
}

#######################################
# let user select a modified file interactively
# Arguments:
#   $1: the helper message to display in the fzf header
#   $2: display mode of modified files.
#     default: true
#     all: display all modified, include staged and unstaged
#     staged: display only staged files
#     unstaged: display only unstaged files
#   $3: output_format
#     default: name
#     name: formatted name of the file
#     raw: raw file name with status
#   $4: if exists, don't do multi selection, do single
# Outputs:
#   the selected file path
#   e.g.$HOME/.config/nvim/init.vim
#######################################
function get_modified_file() {
  local header="${1:-select a modified file}"
  local display_mode="${2:-all}"
  local output_format="${3:-name}"
  set_fzf_multi "$4"
  git --git-dir="${DOTBARE_DIR}" --work-tree="${DOTBARE_TREE}" \
    status --porcelain \
    | awk -v display_mode="${display_mode}" '{
        if ($0 ~ /^[A-Za-z][A-Za-z].*$/) {
          print "\033[32m" substr($0, 1, 1) "\033[31m" substr($0, 2) "\033[0m"
        } else if ($0 ~ /^[A-Za-z][ \t].*$/) {
          if (display_mode == "all" || display_mode == "staged") {
            print "\033[32m" $0 "\033[0m"
          }
        } else {
          if (display_mode == "all" || display_mode == "unstaged") {
            print "\033[31m" $0 "\033[0m"
          }
        }
      }' \
    | fzf --header="${header}" --preview "echo {} \
        | awk '{sub(\$1 FS,\"\");print \$0}' \
        | xargs -I __ git --git-dir=${DOTBARE_DIR} --work-tree=${DOTBARE_TREE} \
            diff HEAD --color=always -- ${DOTBARE_TREE}/__ \
        | ${DOTBARE_DIFF_PAGER}" \
    | awk -v home="${DOTBARE_TREE}" -v format="${output_format}" '{
        if (format == "name") {
          $1=""
          gsub(/^[ \t]/, "", $0)
          gsub(/"/, "", $0)
          print home "/" $0
        } else {
          print $0
        }
      }'
}

#######################################
# let user select a stash interactively
# Arguments:
#   $1: the help message to display in header
#   $2: if exists, don't do multi select, only allow single selection
# Outputs:
#   the selected stash identifier
#   e.g. stash@{0}
#######################################
function get_stash() {
  local header="${1:-select a stash}"
  set_fzf_multi "$2"
  git --git-dir="${DOTBARE_DIR}" --work-tree="${DOTBARE_TREE}" \
    stash list \
    | fzf --header="${header}" --preview "echo {} \
        | awk '{
            gsub(/:/, \"\", \$1)
            print \$1
          }' \
        | xargs -I __ git --git-dir=${DOTBARE_DIR} --work-tree=${DOTBARE_TREE} \
            stash show -p __ --color=always \
        | ${DOTBARE_DIFF_PAGER}" \
    | awk '{
        gsub(/:/, "", $1)
        print $1
      }'
}

#######################################
# Using git grep to find word within
# all tracked files in the bare repo.
# Arguments:
#   $1: the help message to display in header
#   $2: the fzf delimiter to start searching, default is 3
#   $3: if exists, don't do multi select, only allow single selection
# Outputs:
#   the selected file name with it's line number and line, separated by ":"
#   e.g. .bash_profile:1:echo hello
#######################################
function grep_words() {
  local header="${1:-select matches to edit}"
  local delimiter="${2:-3}"
  set_fzf_multi "$2"
  cd "${DOTBARE_TREE}" || exit
  git --git-dir="${DOTBARE_DIR}" --work-tree="${DOTBARE_TREE}" \
    grep --line-number -- . \
    | fzf --delimiter : --nth "${delimiter}".. --header="${header}" \
        --preview "${mydir}/../helper/preview.sh ${DOTBARE_TREE}/{}" \
    | awk -F ":" -v home="${DOTBARE_TREE}" '{
        print home "/" $1 ":" $2
      }'
}
