#!/usr/bin/env bash
#==============================================================#
#
# AUTHOR:
#     Bryan Jenks
#         - www.bryanjenks.xyz
#         - https://github.com/tallguyjenks/flash.sh
#
# SOURCE:
#     This file is based off of the one presented in this YouTube Video by nixcasts:
#         - https://www.youtube.com/watch?v=lX8jqo70r1I
#
# PURPOSE:
#     To have a command line flash card tool with minimal code, plain text input and output
#
# VISION:
#     The goal i have for this script is a basic level emulation of ANKI to where i have a way to
#     keep track of a score for each item in each selected deck so that it can pick from a selection
#     of the lowest scoring items and shuf them to the user for reenforcement of active recall.
#
# DEPENDENCIES:
#     fzf - https://github.com/junegunn/fzf
#     bat - https://github.com/sharkdp/bat
#==============================================================#

# USER CUSTOMIZABLE VARIABLES
    CARD_POOL_SIZE=10 # How large the pool size is for shuf to draw from
    SEARCH_DEPTH=999  # How many levels to recursively search for .csv's in .local/share/flash

# ANSI FOREGROUND ESCAPE COLORS
    RED='\033[0;31m'
    LRED='\033[1;31m'
    YELLOW='\033[1;33m'
    GREEN='\033[0;32m'
    LGREEN='\033[1;32m'
    LBLUE='\033[1;34m'
    CYAN='\033[0;36m'
    LCYAN='\033[1;36m'
    ORANGE='\033[0;33m'
    LGREY='\033[0;37m'
    WHITE='\033[1;37m'
    NC='\033[0m' # No Color

# ANSI BACKGROUND ESCAPE COLORS
    WHITEBG='\033[1;47m'
# FONT FORMAT EXCAPE CODES
    BOLD='\e[1m'

# Remember User's Starting Directory
    PWD="$(pwd)"
# Where Decks Are Located for linux & mac
# Prefer XDG Configuration before hard coded file paths
    OS="$(uname)"
    case "$OS" in
        "Darwin") DIR="${XDG_DATA_HOME:-$HOME/Library/Application Support}/flash" && alias shuf=gshuf ;;
        "Linux") DIR="${XDG_DATA_HOME:-$HOME/.local/share}/flash" ;;
        *) DIR="${XDG_DATA_HOME:-$HOME/.local/share}/flash" ;;
    esac
# Where the example deck will be placed and named
    EXAMPLE_DECK="$DIR/deck.csv"
# Track High score
    HIGH_SCORE="$DIR/.highscore"
# Track number of cards reviewed per session
    REVIEW_LOG="$DIR/.reviews"
# Iterator for Count of cards reveiwed
    COUNTER=0
# Success message of setup process
DIR_MADE_MSG="
Your ${LRED}$DIR${NC} directory has been made and
your ${ORANGE}deck.csv${NC} file is ready for you to enter your flashcard data
If you want to see information about ${ORANGE}fla.sh${NC} then use the option
${YELLOW}flash -i${NC}.
"
# User has .local/share directory but no decks inside
NO_DECKS="
No decks were found, please make a new deck
using ${ORANGE}:${NC} as a delimiter in a ${ORANGE}.csv${NC} file in
the ${LRED}$DIR${NC} directory.

An example of a card:
${GREEN}Math:What is the square root of 4?:2:0${NC}
"
# The example flashcard deck to be made
DECK_TEMPLATE="History:When was the declaration of independence signed?:1776:0
Math:What is the square root of 4?:2:4
Science:What is the charge on a proton?:Positive 1:2
Philosophy:What was Socrates known as?:The Gadfly of Athens:0
Programming:What is the typical starting index of an array?:0:5
History:What did Abraham Lincoln typically keep in his hat?:Mail:0
Math:What is the value of PI to 2 decimal places?:3.14:4
Science:What is the charge on an electron?:Negative 1:3
Programming:What does OOP stand for?:Object Oriented Programming:2
History:What did Socrates drink to commit suicide?:Hemlock Tea:2
Math:What is the general equation for the slope of a line?:y=mx+b:4
Science:What is the charge on a neutron?:Neutral:1
Programming:What is Vim?:God's Text Editor:999
History:What were the British known by during the American Revolution?:The Redcoats:1
Math:What is the value of this equation - log10(100)?:2:0
Science:What are protons and neutrons made of?:quarks:5
Programming:What does RAM stand for?:Random Access Memory:1
History:What was the year 2000 also known as?:Y2K:0
Math:What is the formula for the mean?:Sum/count:4
Science:What is cold?:The absense of heat:3
Programming:What languages are the worst?:Proprietary:999
History:When did man land on the moon?:1969:4
Math:10^3=?:1000:1
Science:The _____ ______ Project mapped all of man's genes.:Human Genome:3
Programming:What is the best computer to program on?:Thinkpad:999
History:When was fla.sh created?:April 2020:999
Math:What do you call a number only divisible by itself and 1?:Prime:0
Science:What is the distance between the Earth and Sol called?:An Astronomical Unit (AU):1
Programming:What is the best operating system?:Arch, because BTW i run Arch:999"

# Define setup process in a function and create necessary files for user
setup(){\
    mkdir "$DIR" && \
    touch {"$EXAMPLE_DECK","$HIGH_SCORE","$REVIEW_LOG"} && \
    echo "$DECK_TEMPLATE" >> "$EXAMPLE_DECK" && \
    echo -e "$DIR_MADE_MSG" ;}

# Test if .local/share exists and wether to offer setup process
if [ ! -d "$DIR" ];then
    echo -e "No ${LRED}$DIR${NC} directory, make it? ${LGREEN}Y${NC}/${LRED}N${NC}"
    # shellcheck disable=SC2162
    read RESPONSE
        case "$RESPONSE" in
            [QqNn]) exit ;;
            [Yy]) setup && exit ;;
            *) echo -e "invalid choice, please select either ${LGREEN}y${NC} or ${LRED}n${NC}" && exit ;;
        esac
fi

# go to the flashcard decks directory
    cd "$DIR" || exit

# If there are no flashcard decks available return user to starting location
# while also displaying explanatory text of issue
if [ "$(find . -maxdepth "$SEARCH_DEPTH" -iname "*.csv" | wc -l)" = 0 ]; then
    echo -e "$NO_DECKS" && cd "$PWD" && exit
fi

# if highscore file was removed, remake it.
if [ ! -e "$HIGH_SCORE" ]; then
    touch "$HIGH_SCORE"
fi

# if reviewed file was removed, remake it.
if [ ! -e "$REVIEW_LOG" ]; then
    touch "$REVIEW_LOG"
fi

print_usage() {
    echo -e "\n${LCYAN}fla.sh --- Flash card system by Bryan Jenks${NC} ${LBLUE}<BryanJenks@protonmail.com>${NC}\n\n${YELLOW}${BOLD}Usage:${NC}\n\t${GREEN}flash -h:${NC} Print this help text\n\t${GREEN}flash -i:${NC} Print Information about the flashcard system\n\t${GREEN}flash -v:${NC} Print version Number\n\n"
}

print_info() {
echo -e "\nThis flash card system works via colon ${YELLOW}:${NC} seperated ${YELLOW}.csv${NC} files\n
with entries that look like this: ${LGREY}category:question:answer:0${NC}\n
These ${YELLOW}.csv${NC} files should be stored in your

\t${YELLOW}\$XDG_DATA_HOME/flash${NC}
OR
\t${YELLOW}~/.local/share/flash${NC} for Linux
\t${YELLOW}~/Library/Application Support/flash${NC} for mac
-------------------------------------------------------------------------------
${LCYAN}Exiting:${NC}\n
\tYou can exit the application at any time by pressing either ${RED}q${NC} or ${RED}Q${NC}

${LCYAN}Usage:${NC}\n
\tYou will first be prompted with a question and will need to press ${RED}[Enter]${NC}
\tto reveal the answer. Once the answer is revealed you will need to either
\tquit, or give a rating to how difficult the question was.

${LCYAN}The Scoring System:${NC}\n
\tThe last field in the ${YELLOW}.csv${NC} files is the current point score of the 'card'
\tEvery time you rate a card the point score will either increase, decrease,
\tor stay the same. zero is the lowest value, and the upper limit is over
\t100 billion.

\t${CYAN}Scoring Results:${NC}
\t\t[${LRED}Hard${NC}]\t\t-2 points
\t\t[${RED}Difficult${NC}]\t-1 point
\t\t[${YELLOW}Normal${NC}]\tNo Change
\t\t[${GREEN}Mild${NC}]\t\t+1 point
\t\t[${LGREEN}Easy${NC}]\t\t+2 points

\tThe cards with the lowest scores (you scored them as ${LRED}Hard${NC}/${RED}Difficult${NC}) are
\tsorted to the top, a pool of them are picked and from that pool 1 card is
\trandomly drawn. As you become more familair with the material and rate it
\tas easier, the point values will go up and the cards will appear less
\tfrequently making room for those cards that are still difficult and have
\tlower point values.

    It is a good idea to start all cards off at ${YELLOW}0${NC} initially so that they all
    have an equal chance of being drawn initially.
"
}

while getopts 'hiv' flag; do
  case "${flag}" in
    h) print_usage && exit ;;
    i) print_info && exit ;;
    v) echo -e "\n${YELLOW}fla.sh Current Version:${NC} ${RED}1.1${NC}\n" && exit ;;
    *) print_usage && exit 1 ;;
  esac
done


# Show pretty FZF preview of decks using BAT
    DECK="$(find . -maxdepth "$SEARCH_DEPTH" -iname "*.csv" | fzf --preview='bat --theme="Solarized (dark)" --style=numbers --color=always {} | head -500')"

# If no deck is selected in fzf menu
# return user to start location
# and exit quietly
if [ -z "$DECK" ]; then
    cd "$PWD" && exit
fi

main(){
    IFS=$':'
    # shellcheck disable=SC2162
    read -a q <<< "$(sort "$DECK" -n --field-separator=: --key=4 | head -n "$CARD_POOL_SIZE"| shuf -n 1)"
    clear
    echo -e "${WHITEBG}          Fla.sh - Flash Cards In Your Terminal            ${NC}
${ORANGE}${BOLD}Cards Reviewed:${BOLD}${NC}\t$COUNTER
\t${ORANGE}${BOLD}High Score:${BOLD}${NC}\t$(cat "$HIGH_SCORE")
\t${ORANGE}${BOLD}Avg review:${BOLD}${NC}\t$(awk '{ sum += $7; n++ } END { if (n > 0) print sum / n; }' "$REVIEW_LOG")

${LGREY}Category:${NC}\n\t\t${q[0]}
${LGREY}Question:${NC}\n\t\t${q[1]}


${LGREY}Press [Enter] to continue...${NC}"
    # shellcheck disable=SC2162
    read -sn 1 NEXT
    while [ ! "$NEXT" = "" ] && [ ! "$NEXT" = q ] && [ ! "$NEXT" = Q ]; do
        # shellcheck disable=SC2162
        read -sn 1 NEXT
    done
    clear
    echo -e "${WHITEBG}          Fla.sh - Flash Cards In Your Terminal            ${NC}
${ORANGE}${BOLD}Cards Reviewed:${BOLD}${NC}\t$COUNTER
\t${ORANGE}${BOLD}High Score:${BOLD}${NC}\t$(cat "$HIGH_SCORE")
\t${ORANGE}${BOLD}Avg review:${BOLD}${NC}\t$(awk '{ sum += $7; n++ } END { if (n > 0) print sum / n; }' "$REVIEW_LOG")

${LGREY}Category:${NC}\n\t\t${q[0]}
${LGREY}Question:${NC}\n\t\t${q[1]}
"
    if [ "$NEXT" = q ] || [ "$NEXT" = Q ]; then
        add_usage_entry && cd "$PWD" && clear && exit
    fi
    echo -e "${LGREY}Answer:${NC}\n\t\t${q[2]}

${WHITEBG}${WHITE}===========================================================${NC}

${LGREY}How Difficult Was This Question?${NC}

${LRED}Hard${NC} [1]   ${RED}Difficult${NC} [2]   ${YELLOW}Normal${NC} [3]   ${GREEN}Mild${NC} [4]   ${LGREEN}Easy${NC} [5]

${LGREY}Select a number to continue, or${NC} ${LRED}Q${NC} ${LGREY}to quit...${NC}"
    # shellcheck disable=SC2162
    read -sn 1 DIFFICULTY_SCORE
    while [[ ! "$DIFFICULTY_SCORE" =~ [12345qQ] ]]; do
        # shellcheck disable=SC2162
        read -sn 1 DIFFICULTY_SCORE
    done
    if [ "$DIFFICULTY_SCORE" = q ] || [ "$DIFFICULTY_SCORE" = Q ]; then
        add_usage_entry && cd "$PWD" && clear && exit
    fi
    clear

    COUNTER="$((COUNTER+1))" # Increment count for card review count increment

    if [ "${q[3]}" = 0 ]; then
        NEW_ITEM_SCORE=0
        case "$DIFFICULTY_SCORE" in
            [123]) NEW_ITEM_SCORE=0 ;;#HARD DIFFICULTY & NORMAL
            4) NEW_ITEM_SCORE=1 ;;#MILD
            5) NEW_ITEM_SCORE=2 ;;#EASY
            *) NEW_ITEM_SCORE=0 ;;#INVALID
        esac

    elif [ "${q[3]}" = 1 ]; then
        case "$DIFFICULTY_SCORE" in
            1) NEW_ITEM_SCORE=0 ;;#HARD
            2) NEW_ITEM_SCORE=0 ;;#DIFFICULT
            3) NEW_ITEM_SCORE=1 ;;#NORMAL
            4) NEW_ITEM_SCORE=2 ;;#MILD
            5) NEW_ITEM_SCORE=3 ;;#EASY
            *) NEW_ITEM_SCORE=1 ;;#INVALID
        esac
    else
        case "$DIFFICULTY_SCORE" in
            1) NEW_ITEM_SCORE="$((q[3]-2))" ;;#HARD
            2) NEW_ITEM_SCORE="$((q[3]-1))" ;;#DIFFICULTY
            3) NEW_ITEM_SCORE="${q[3]}" ;;#NORMAL
            4) NEW_ITEM_SCORE="$((q[3]+1))" ;;#MILD
            5) NEW_ITEM_SCORE="$((q[3]+2))" ;;#EASY
            *) NEW_ITEM_SCORE="${q[3]}" ;;#INVALID
        esac
    fi

    # Update item score for each flashcard item
    sed -i "s/${q[0]}:${q[1]}:${q[2]}:${q[3]}/${q[0]}:${q[1]}:${q[2]}:$NEW_ITEM_SCORE/g" "$DECK"
    # If no highscore currently set, set it.
    if [ -z "$(cat "$HIGH_SCORE")" ]; then
        echo "$COUNTER" > "$HIGH_SCORE"
    fi

    # If Cards Reviewed > Current High Score, Update
    if [ "$COUNTER" -gt "$(cat "$HIGH_SCORE")" ]; then
        echo "$COUNTER" > "$HIGH_SCORE"
    fi

}

add_usage_entry(){
    if [ ! "$COUNTER" = 0 ]; then
        # Create a New Entry
        TIME_STAMP=$(date --rfc-3339=seconds)
        REVIEWED_DECK="$(echo "$EXAMPLE_DECK" | awk -F/ '{print $7}')"
        printf -v ENTRY "TimeStamp: %s Deck: %s cardsReviewed: %s" "$TIME_STAMP" "$REVIEWED_DECK" "$COUNTER"
        echo "$ENTRY" >> "$REVIEW_LOG"
    fi
}

while true; do
        main
done
