#!/usr/bin/env bash


# usage info
usage() {
  cat <<EOF
  Usage: git log [options]
  Options:
    -d, --date              Show logs for last n days.
    -a, --author            Author name.
    -f, --fetch             fetch commits.
    -h, --help              This message.
    --                      End of options.
EOF
}


# Global variables
SINCE="1 days ago" # show logs for last day by default
AUTHOR=""
FETCH=false
GIT_FORMAT=""
GIT_LOG=""
COMMITS=""
VERSION="1.0.1"

# Are we in a git repo?
if [[ ! -d ".git" ]]; then
  echo "abort: not a git repository." 1>&2
  exit 1
fi

# Parse options
while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do
  case $1 in
    -v | --version )
      echo $version
      exit
      ;;
    -d | --date )
      SINCE="$2 days ago"
      shift;
      ;;
    -a | --author )
      AUTHOR="$2"
      shift
      ;;
    -f | --fetch )
      FETCH=true
      ;;
    -h | --help )
      usage
      exit
      ;;
    * )
      echo "abort: unknown argument" 1>&2
      exit 1
  esac
  shift
done
if [[ "$1" == "--" ]]; then shift; fi


# Colored output.
function colored() {
  GREEN=$(tput setaf 4)
  YELLOW=$(tput setaf 3)
  NORMAL=$(tput sgr0)
  REVERSE=$(tput rev)
} 

# Uncolored output.
function uncolored() {
  GREEN=""
  YELLOW=""
  NORMAL=""
  REVERSE=""
}

# Enable colors if supported by terminal.
if [[ -t 1 ]] && [[ -n "$TERM" ]] && which tput &>/dev/null && tput colors &>/dev/null; then
  ncolors=$(tput colors)
  if [[ -n "$ncolors" ]] && [[ "$ncolors" -ge 8 ]] ; then
    colored
  else
    uncolored
  fi
else
  uncolored
fi

# Set AUTHOR to current user if no param passed or display for all users if param equal to "all". 
if [[ ! -n $AUTHOR ]]; then
    AUTHOR=$(git config user.name 2>/dev/null)
elif [[ $AUTHOR = "all" ]]; then
    AUTHOR=".*"
fi

# Fetch changes before.
if [[ $FETCH == true ]]; then
    echo "${GREEN}Fetching changes...${NORMAL}"
    git fetch --all &> /dev/null
    tput cuu1
    tput ed # clear screen
fi

# Log template.
GIT_FORMAT="%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset"

# Log command.
GIT_LOG="git log --pretty=format:'${GIT_FORMAT}'  
           --author \"$AUTHOR\"
           --since \"$SINCE\" --abbrev-commit"

# Change temporary the IFS to store GIT_LOG's output into an array.
IFS=$'\n'
COMMITS=($(eval ${GIT_LOG} 2>/dev/null))
unset IFS

NI=${#COMMITS[@]} # Total number of items.
SN=$(( `tput lines` - 1 )) # Screen number of lines.
TN=$(( $NI < $((SN -1)) ? $NI : $((SN -1)))) # Number of lines that we will display.

# If there is no items, exit.
[[ $NI = 0 ]] && echo "${YELLOW}The contributor \"${AUTHOR}\" did nothing during this period.${NORMAL}" && exit 0

# Set keys.
au="`echo -e '\e[A'`" # arrow up
ad="`echo -e '\e[B'`" # arrow down
ec="`echo -e '\e'`"   # escape
nl="`echo -e '\n'`"   # newline

# Create a temporary lesskey file to change the keybindings so the user can use the TAB key to quit less. (more convenient)  
echo "\t quit" | lesskey -o $HOME/.lsh_less_keys_tmp -- -

## Get commit's diff.
function get_diff() {
    ELT="$(echo "${COMMITS[$CP-1]}" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g")" # remove colors escape codes
    DIFF_TIP=${ELT:0:7}
    DIFF_CMD="git show $DIFF_TIP --color=always"
    DIFF=$(eval ${DIFF_CMD} 2>/dev/null)
    DIFF_LINES_NUMBER="$(echo "$DIFF" | wc -l)"
}

## This function will print the diff according the commit's tip. If the diff is too long, the result will be displayed using 'less'. 
function print_diff() {
    
    get_diff

    if [[ $(( TN + DIFF_LINES_NUMBER )) -ge $(( `tput lines` - 1 )) ]]; then
	echo -e "$REVERSE ${COMMITS[$CP - 1]}\n\n $DIFF" | less -r -k $HOME/.lsh_less_keys_tmp
    else 
	stop=false
	tput ed
	for i in `seq 1 $TN`
	do
	    echo -n "$NORMAL"
	    [[ $CP == $i ]] && echo -n "$REVERSE"
	    
	    echo "${COMMITS[$i - 1]}"
	
	    if [[ $CP == $i ]]; then
	        echo "$DIFF"
	    fi
	done

	while ! $stop
	do
	    read -sn 1 key
	    case "$key" in
		"$nl")
		    stop=true
		    ;;
		"q")
		    stop=true
		    END=true
		    ;;
	    esac
	done
	[[ $END = false ]] && tput cuu $(( TN + DIFF_LINES_NUMBER )) && tput ed
	[[ $END = true ]] && tput cuu 1
    fi
}


{ # capture stdout to stderr

tput civis # hide cursor.
CP=1 # current position
SI=1 # index
END=false # end while loop

## Loops, reads inputs and prints commits until user presses 'q' to exit or TAB to show the diff.
while ! $END
do

   for i in `seq $SI $(( $TN + $SI -1 ))`
   do
      echo -n "$NORMAL"
      [[ $CP == $i ]] && echo -n "$REVERSE"

      echo "${COMMITS[$i - 1]}"
   done

   read -sn 1 key
   [[ "$key" == "$ec" ]] &&
   {
      read -sn 2 k2
      key="$key$k2"
   }

   case "$key" in

      "$au")
         CP=$(( CP - 1 ))
         [[ $CP == 0 ]] && [[ $SI=1 ]] && [[ $TN == $(( SN - 1 )) ]] && CP=$NI && SI=$(( NI - SN + 2 ))
         [[ $CP == 0 ]] && [[ $SI=1 ]] && [[ $TN == $NI ]] && CP=$TN
         [[ $CP == $(( SI - 1 )) ]] && [[ $SI != 1 ]] && SI=$(( SI - 1 ))
	 tput cuu $TN
         [[ $SI != 1 ]] && tput ed # clear screen
         ;;

      "$ad")
	 CP=$(( CP + 1 ))
         [[ $CP == $(( NI + 1 )) ]] && CP=1 && SI=1 
         [[ $CP == $(( SN + SI - 1)) ]] && [[ $TN == $(( SN - 1 )) ]] && SI=$(( SI + 1 ))
	 tput cuu $TN
         [[ $SI != 1 ]] && tput ed # clear screen
         [[ $SI = 1 ]] && [[ $CP = 1 ]] && tput ed # clear screen
         ;;

      "$nl")
	  tput cuu $TN
          print_diff 
         ;;

      "q")
         si=false
         END=true
	 tput cuu 1
         ;;

      * )
	 tput cuu $TN
   esac

done

rm $HOME/.lsh_less_keys_tmp # remove temporary less keybindings
tput cnorm # unhide cursor
echo "$NORMAL" # normal colors

} >&2 # END capture

