#!/usr/bin/env bash
# debug settings
set -e    #exit on error for each command
#set -x
#trap read debug

main() {
  # global variables
  current_glyphacter_empty=0
  weeks_max=52
  weeks_min=0
  weeks=$((weeks_max-weeks_min))
  week_labels=("Sun" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat")
  glyph="square"
  xgap=" "
  ygap="\n"
  user=""
  # user="tonsky"

  # Boolean flags
  portrait=1
  labels=1
  labelTotalContributions=1
  double=1
  debug=1
  warning=0
  scheme_gh_original=("#ebedf0" "#9be9a8" "#40c463" "#30a14e" "#216e39")
  scheme_gh_contrast=("#333333" "#9be9a8" "#40c463" "#30a14e" "#216e39")
  scheme_vibrant=("#333333" "#F9ED69" "#F08A5D" "#B83B5E" "#6A2C70")
  scheme_blackAwhite=("#333333" "#707070" "#a0a0a0" "#d0d0d0" "#ffffff")
  scheme_dracula=("#282a36" "#50fa7b" "#ff79c6" "#bd93f9" "#6272a4")
  scheme_north=("#333333" "#DBE2EF" "#3282B8" "#3F72AF" "#112D4E")
  scheme_gold=("#333333" "#FDEEDC" "#FFD8A9" "#F1A661" "#E38B29")
  scheme_sunset=("#333333" "#F67280" "#C06C84" "#6C5B7B" "#355C7D")
  scheme_mint=("#333333" "#5AFEAD" "#21B475" "#1D794B" "#294B36")
  scheme="gh_contrast"


  parse_args "$@"
  parse_glyph
  parse_scheme
  fetch_contributions

  # local variables
  terminal_width=$(tput cols)
  output_width=$(calculate_output_width "$weeks")
  
  local wdouble=1
  local wlabel=0
  local wxgap=1
  [ $double -eq 0 ] && wdouble=2
  [ $labels -eq 0 ] && wlabel=4
  [ -z "$xgap" ] && wxgap=0
  [ $debug -eq 0 ] && printf "
  weeks: $weeks
  double: $wdouble
  wxgap: $wxgap
  wlabel: $wlabel
  ((weeks*(wdouble+wxgap)+wlabel+2))
  \n"


  [ $debug -eq 0 ] && printf "terminal: $terminal_width, calculated-output: $output_width\n"

  force=1
  # automatic resizing
  if [ $terminal_width -lt "$output_width" ] && [ $force -eq 1 ] && [ $portrait -eq 1 ]; then    # t < O
    # adjust weeks
    # x=83-2-4/(2+1)
    #cstep1="$((terminal_width-2-wlabel))"
    #cstep2="$(((wdouble+wxgaps)))"
    #printf "weeks = $cstep1 / $cstep2 \n"
    #local output_width="$(calculate_output_width $weeks)"
    #printf "$output_width\n"
    weeks="$(((terminal_width-wdouble-wxgap-wlabel)/(wdouble+wxgap)))"
    weeks_min="$((weeks_max-weeks))"
    [ $warning -eq 0 ] && printf "exceeding terminal_width, resizing to $weeks weeks\n"
  fi
  
  [ $double -eq 0 ] && render_graph 1 || render_graph 0

  [ $debug -eq 0 ] && printf "weeks: $weeks, w-min: $weeks_min, w-max: $weeks_max \n"
  return 0
}

fetch_contributions() {
  local query
  if [[ -z "$user" ]]; then
    query='{ viewer { contributionsCollection { contributionCalendar { totalContributions weeks { contributionDays { color } } } } } }'
    result=$(gh api graphql -f query="$query")
  else
    query='query($user: String!) { user(login: $user) { contributionsCollection { contributionCalendar { totalContributions weeks { contributionDays { color } } } } } }'
    result=$(gh api graphql -F "user=$user" -f query="$query")
  fi
  
  # Extract totalContributions
  totalContributions=$(echo "$result" | grep -o '"totalContributions":[0-9]*' | grep -o '[0-9]*')

  # Extract all colors (hex codes) into an array
  readarray -t days < <(echo "$result" | grep -o '"color":"#[a-fA-F0-9]*"' | grep -o '#[a-fA-F0-9]*')
}

parse_args() {
  while [ $# -gt 0 ]; do
    case "$1" in
      -x  | --xgap)      xgap="";                                                                                   shift 1;;
      -y  | --ygap)      ygap="\n\n";                                                                               shift 1;;
      -d  | --double)    double=0;                                                                                  shift 1;;
      -l  | --labels)    labels=0;                                                                                  shift 1;;
      -t  | --total)     labelTotalContributions=0;                                                                 shift 1;;
      -p  | --portrait)  portrait=0;                                                                                shift 1;;
      -D  | --Debug)     debug=0;                                                                                   shift 1;;
      -W  | --Warning)   warning=1;                                                                                 shift 1;;
      -xy |  -yx)        xgap=" "; ygap="\n\n";                                                                     shift 1;;
      -s  | --scheme)    scheme="$2";  [ -z $2 ] && h_schemes >&2 && exit 1;                                        shift 2;;
      -g  | --glyph)     glyph="$2";   [ -z $2 ] && h_glyphs >&2 && exit 1;                                         shift 2;;
      -u  | --user)      user="$2";    [ -z $2 ] && printf "\nError: no username given\n" >&2 && exit 1;            shift 2;;
      -w  | --weeks)     weeks="$2";   weeks_min="$(($weeks_max-weeks))"; [ -z $2 ] && echo "error" >&2 && exit 1;  shift 2;;
      -h  | --help)      h_usage >&2; h_args >&2; h_glyphs >&2; h_schemes >&2;                                       exit 0;;
      *)                 h_usage >&2; h_error >&2;                                                                   exit 1;;
    esac
  done
  return 0
}

parse_glyph() {
  case "$glyph" in
    block)
      block_full="█"
      block_empty="$block_full";;
    square)
      block_full="◼︎"
      block_empty="$block_full";;
    dot)
      block_full="•"
      block_empty="$block_full";;
    fisheye)
      block_full="◉"
      block_empty="●";;
    diamond)
      block_full="♦︎"
      block_empty="$block_full";;
    plus)
      block_full="✚"
      block_empty="•";;
    *)
      echo "error: glyph '$glyph' not recognized" >&2
      h_glyphs >&2
      exit 1;;
  esac
  return 0
}

parse_scheme() {
  local var=$scheme[@]
  eval current_scheme=\${scheme_${scheme}[@]}
  [ -z "$current_scheme" ] && eval 'echo "error: scheme '$scheme' not recognized" >&2;h_schemes >&2;exit 1;'
  return 0
}

render_graph() {
  if [[ $portrait -eq 0 ]]; then
    render_graph_portrait $1
  else
    render_graph_landscape $1
  fi
  return 0
}

render_graph_landscape() {
  #printf "\nUsed function\n\n"
  [[ $labelTotalContributions -eq 0 ]] && printf "$totalContributions contributions in the last year\n\n"
  for d in {0..6}; do
    [[ $labels -eq 0 ]] && printf "%s " "${week_labels[$d]}"
    for ((w = weeks_min; w <= weeks_max; w++)); do
      print_cell $1
    done
    printf "$ygap"
  done
  printf "\n"
  return 0
}

render_graph_portrait() {
  #printf "\nUsed function\n\n"
  for ((w = weeks_min; w <= weeks_max; w++)); do
    for d in {0..6}; do
      print_cell $1
    done
    printf "$ygap"
  done
  printf "\n"

  return 0
}

print_cell() {
  # hex = "#xxyyzz"
  hex="${days[w*7+d]}"
  [ "$debug" -eq 0 ] && printf "before: $hex"
  [ ! -z "$hex" ] && hex="$(color_mod $hex ${current_scheme[@]})"
  [ "$debug" -eq 0 ] && printf "after: $hex"
  for i in $(seq 0 $1);do
    [ ! -z "$hex" ] && printf "\e[38;2;%d;%d;%dm%s\e[m%s" "$((16#${hex:1:2}))" "$((16#${hex:3:2}))" "$((16#${hex:5:2}))" "$(glyph $hex $scheme_vibrant)"
  done
  [ ! -z "$hex" ] && printf "$xgap"

  return 0
}

calculate_output_width() {
  #calclogic
  #x=weeks*(wdouble+wxgaps)+wlabel+2
  #(x-2-wlabel)/(wdouble+wxgaps)=weeks
  local wdouble=1
  local wlabel=0
  local wxgap=1
  [ $double -eq 0 ] && wdouble=2
  [ $labels -eq 0 ] && wlabel=4
  [ -z "$xgap" ] && wxgap=0
  #printf "$(($1*(wdouble+wxgap)+wlabel+2))"
  printf "$(($1*(wdouble+wxgap)+wlabel+wdouble+wgap))"
  return 0
}

color_mod() {
  local gh_default=("#ebedf0" "#9be9a8" "#40c463" "#30a14e" "#216e39")
  local hex="$1"
  shift
  local scheme=("$@")
  case "$hex" in
    "${gh_default[0]}")   printf "${scheme[0]}";;
    "${gh_default[1]}")   printf "${scheme[1]}";;
    "${gh_default[2]}")   printf "${scheme[2]}";;
    "${gh_default[3]}")   printf "${scheme[3]}";;
    "${gh_default[4]}")   printf "${scheme[4]}";;
    *)                    printf "$hex";;
  esac

  return 0
}

glyph() {
  local hex="$1"
  local scheme="$2"
  if [ "$hex" = "${scheme[0]}" ]; then
    echo "$block_empty"
  else
    echo "$block_full"
  fi

  return 0
}

# msgs
h_usage() {
  printf "Usage: gh contribs [-h] [-s <scheme>] [-g <glyph>] [-u <user>] [-w <weeks>] [-x] [-y] [-d] [-l] [-t] [-p] [-D] [-W]\n\n"
  return 0
}

h_error() {
  printf "Error: Invalid input!\n\n"
  return 0
}

h_warning() {
  printf "Warning: Terminal too thin, (-x --xgap) ignored!\n"
  return 0
}

#-c | --config <path/to/cfg> Specify Config
h_args() {
  printf "Arguments:

  -h | --help                 Displays this help.
  -s | --scheme <name>        Color scheme
  -g | --glyph <char>         Change character
  -u | --user <user>          Show graph for other users
  -w | --weeks <weeks>        Set range of weeks to be displayed
  -x | --xgap                 Remove gap between each column
  -y | --ygap                 Put a gap between each row
  -d | --double               Enable doubling Glyph
  -l | --labels               Print labels
  -t | --total                Print total contributions
  -p | --portrait             Set output orientation
  -D | --Debug                Enable additional debug output
  -W | --Warning              Disable warning output
  \n"
  return 0
}

h_glyphs() {
  printf "glyphs:\nsquare dot fisheye diamond plus block\n\n"
  return 0
}

h_schemes() {
  printf "Schemes:\ngh_original gh_contrast vibrant blackAwhite dracula north gold sunset mint\n\n"
  return 0
}

h_files() {
  printf "Files:\n~/.config/gh-contribs/init.cfg\n\n"
  return 0
}

main "$@"
