#!/bin/sh
# --------------------------------------
# name: farend
# desc: a simple todo system in shell
# main: jaidyn ann <jadedctrl@teknik.io>
# --------------------------------------

# Take in everything in stdin, then print
# (Useful for piping something into a variable)
function reade {
	local stack=""
	while read input; do
		stack="$(printf '%s\n%s' "$stack" "$input")"
	done
	echo "$stack"
}



# --------------------------------------
# BASIC MATH

# Add together two numbers
function add {
	local a="$1"; local b="$2"
	echo "$a + $b" \
	| bc
}

# Subtract two numbers
function subtract {
	local a="$1"; local b="$2"
	echo "$a - $b" \
	| bc 
}

# Increment a number by one
function inc {
	local a="$1"
	add 1 "$a"
}

# Decrement a number by one
function dec {
	local a="$1"
	subtract "$a" 1
}


# Format a number to be a certain amount of digits long
# (done by prepending zeroes)
function digits {
	local number="$(reade)"
	local digits="$1"

	if test "$(dec "$(echo "$number" | wc -c )")" -lt "$(inc "$digits")"
	then
		local i=1;
		while test $i -lt "$digits"; do
			printf "0";
			i="$(inc "$i")"
		done
		printf %s $number
	else
		printf %s $number;
	fi
}



# --------------------------------------
# DATE PRIMITIVES

# Return today's date, YYYY-MM-DD
function today {
	date +"%Y-%m-%d"
}


# Return the day of a given date
function date_day {
	local date="$1"
	echo "$date" \
	| awk -F "-" '{print $3}'
}

# Return the month of a given date
function date_month {
	local date="$1"
	echo "$date" \
	| awk -F "-" '{print $2}'
}

 # Return the year of a given date
function date_year {
	local date="$1"
	echo "$date" \
	| awk -F "-" '{print $1}'
}


# Return how many days ought to be in the given month
function month_days {
	local month="$1"
	case "$month" in
		09) echo 30;;
		04) echo 30;;
		06) echo 30;;
		11) echo 30;;
		*)  echo 31;;
	esac
}



# --------------------------------------
# DATE ARITHMETIC

# Add an amount of days to a given date
function add_days {
	local date="$1"
	local days_added="$2"
	local day="$(date_day "$date")"; local month="$(date_month "$date")"
	local year="$(date_year "$date")"

	local new_day="$(add "$day" "$days_added" | digits 2)"

	carry_date "${year}-${month}-$new_day"
}

# Return whether or not a given date is valid
# (I.E., not too month months or days)
function is_balanced_date {
	local date="$1"
	local month="$(date_month "$date")"
	local day="$(date_day "$date")"
	local month_max="$(month_days "$month")"

	if test "$month" -gt 12; then
		return 1
	elif test "$day" -gt "$month_max" ; then
		return 1
	else
		return 0
	fi
}

# Correct an unbalanced date
function carry_date {
	local date="$1"

	is_balanced_date "$date"
	while test "$?" -eq 1; do
		date="$(carry_months "$(carry_days "$date")")"
	done
	echo "$date"
}

# If too many days in a given month, carry them over
function carry_days {
	local date="$1"
	local year="$(date_year "$date")"
	local month="$(date_month "$date")"
	local days="$(date_day "$date")"

	if test "$days" -gt "$(month_days "$month")"; then
		local new_days="$(subtract "$days" "$(month_days "$month")")"
		local new_month="$(add "$month" "1")"
		new_days="$(echo "$new_days" | digits 2)"
		new_month="$(echo "$new_month" | digits 2)"
		echo "${year}-${new_month}-${new_days}"
	else
		echo "$date"
	fi
}

# If too many months in a year, carry them over
function carry_months {
	local date="$1"
	local year="$(date_year "$date")"
	local day="$(date_day "$date")"
	local month="$(date_month "$date")"

	if test "$month" -gt 12; then
		month="$(subtract "$month" 12 | digits 2)"
		year="$(inc "$year")"
	fi
	echo "${year}-${month}-${day}"
}



# --------------------------------------
# TODO HANDLING

# Clean up a todo file for use with the program
function preprocess_todo {
	ignore_todo_blanks \
	| demystify_todo_times
}


# Replace piped todo's vague dates with current dates
function demystify_todo_times {
	local year="$(date_year "$(today)")"
	local month="$(date_month "$(today)")"
	local day="$(date_day "$(today)")"
	sed 's%^\*-%'"$year"'-%g' \
	| sed 's%-\*-%-'"$month"'-%g' \
	| sed 's%-\*%-'"$day"'%g'
}

# Filter out comments and blank lines from piped todo
function ignore_todo_blanks {
	grep -v "^#" \
	| grep -v "^$"
}


# Return all todo lines of the given date
function date_todo_lines {
	local date="$1"
	grep "$date"
}

# Print all todo lines during the giving days following the start-date
function upcoming_todo_lines {
	local todo_file="$1"
	local start_date="$2"
	local limit="$3"

	if test "$limit" -eq 0; then limit="$(inc "$limit")"; fi

	local i="0"
	while test "$i" -lt "$limit"; do
		cat "$todo_file" \
		| preprocess_todo \
		| date_todo_lines "$(add_days "$start_date" "$i")"

		if test "$?" -eq 0 \
			-a "$i" -ne "$(dec "$limit")" \
			-a "$QUIET_MODE" -ne 0
		then	echo "---"; fi

		i="$(inc "$i")"
	done
}


# Print a user-friendly report of upcoming events <3
function generate_report {
	local todo_file="$1"
	local limit="$2"

	local tomorrow="$(add_days "$(today)" 1)"
	local today_lines="$(upcoming_todo_lines "$todo_file" "$(today)" 0)"
	local later_lines="$(upcoming_todo_lines "$todo_file" "$tomorrow" "$limit")"

	if test -n "$today_lines"; then
		echo "$TODAY_MSG"
		echo "$DIVIDER"
		echo "$today_lines"
		if test -n "$later_lines"; then echo ''; fi
	fi

	if test -n "$later_lines"; then
		echo "$LATER_MSG"
		echo "$DIVIDER"
		echo "$later_lines"
	fi
}



# --------------------------------------
# INVOCATION

BIN="$(echo "$0" | sed 's%.*/%%')"
function print_help {
	echo "usage: $BIN [-hq] [-l \$LIMIT] [-L | -D \$MSG] [\$TODO_PATH]"
	exit 2
}


# ------------------
# OPTIONS

DIVIDER="----------------------------------------"
TODO_FILE="$HOME/.todo"
LIMIT=7
TODAY_MSG="TODAY"
LATER_MSG="NEXT EPISODE..."
PAST_MSG="FROM THE GRAVE"
QUIET_MODE=1

while getopts 'l:D:T:L:qh' c; do
	case "$c" in
			l) LIMIT="$OPTARG" ;;
			D) DIVIDER="$OPTARG" ;;
			T) TODAY_MSG="$OPTARG" ;;
			L) LATER_MSG="$OPTARG" ;;
			q) QUIET_MODE=0 ;;
			h) print_help ;;
	esac
done
shift "$(dec "$OPTIND" 1)"

FREE_ARG="$1"
if test -n "$FREE_ARG"; then
	TODO_FILE="$FREE_ARG"
fi


# ------------------
# PROGRAM TYME

if test ! -e "$TODO_FILE"; then
	echo "$TODO_FILE: No such file exists"
	exit 3
fi

if test $QUIET_MODE -eq 1; then
	generate_report "$TODO_FILE" "$LIMIT"
else
	upcoming_todo_lines "$TODO_FILE" "$(today)" "$LIMIT"
fi