#!/bin/bash

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU General Public License for more details.

    # You should have received a copy of the GNU General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Description and overview
# ------------------------
#
# Simplistic Task Manager: a shell script that uses standard UNIX tools
# to control the presentation and contents of a plain text file that
# serves as a task list.
#
# My task list is just a plain text file with one task/sentence per
# line.  There are no fancy formats, no complex dependencies.  STM is
# flexible because it does not enforce a specific method for writing
# tasks.  That is left up to the user's discretion.
#
# The way I define tasks is as follows:
#
# * Each line holds a task
# * Each task starts with a majuscule
# * There is no formal markup, but:
# 	- A task's project/context/category is @context
#	- Tag[s] are #tag or #tag1 #tag2
#	- Date is =YYYY-MM-DD
# * The task's description always comes first
# * Markup is optional and follows in this order: @ # =
#
# Example of a fully marked-up task:
#
# 	Review code @dotfiles #scripts #stm =2019-02-15
#
# This script is part of my dotfiles:
# https://gitlab.com/protesilaos/dotfiles
#
# For a dmenu interface to adding/removing tasks to the same to-do list,
# see my stmmenu, which is also part of my dotfiles (inside the "bin"
# directory).

# Global variables
# ----------------

# Define my task list file.
#
# PRO TIP create your task list in a git-controlled directory (so as not
# to lose things by accident) and then symlink it to the target defined
# below:
#	ln -s /path/to/git/directory/my_task_list ~/.my_task_list
if [ -e "$HOME/.my_task_list" ]; then
	task_list="$HOME/.my_task_list"
else
	touch -h "$HOME/.my_task_list"
	task_list="$HOME/.my_task_list"
fi

# colour definitions for the output: ANSI sequences
my_red='\033[1;31m'
my_green='\033[1;32m'
my_yellow='\033[1;33m'
my_blue='\033[1;34m'
my_magenta='\033[1;35m'
my_cyan='\033[1;36m'
my_nocol='\033[0m'

# the line length for folding stm's output
stm_fold=80

# Functions
# ---------

stm_help() {
	echo -e "${my_cyan}stm${my_nocol} ${my_magenta}[d|due] [e|edit] [l|list] [o|omit] [h|help|-h|--help]${my_nocol}"
	echo ""
	echo -e "Run ${my_green}stm${my_nocol} without arguments to print the task list."
	echo -e "Run ${my_green}stm due${my_nocol} to get only the tasks with a due date."
	echo -e "Run ${my_green}stm edit${my_nocol} open the task list in your EDITOR."
	echo -e "Run ${my_green}stm list${my_nocol} <string> to only show tasks that match the given string."
	echo -e "Run ${my_green}stm omit${my_nocol} <string> to display tasks that **hide** the matching string."
	echo -e "Run ${my_green}stm help${my_nocol} to get this message."
	echo ""
	echo -e "The task list is stored in plain text at ${my_blue}$task_list${my_nocol}."
	echo -e "In case you choose to edit the file, your EDITOR is ${my_blue}$EDITOR${my_nocol}."
	echo ""
	echo "This is STM, the Simplistic Task Manager by Protesilaos Stavrou (GPLv3)."
	echo "My home repo: https://gitlab.com/protesilaos/dotfiles."
	echo ""
	echo "Need ideas for writing good tasks?"
	echo "See: https://protesilaos.com/codelog/2019-02-17-unix-ways-todo/"
}

# The command that parses the output of the to-do list.  To include or
# exclude the matching pattern.
stm_region_parse() {
	if [ "$1" == 'v' ]; then
		grep -v "$stm_region" "$task_list"
	else
		grep "$stm_region" "$task_list"
	fi
}

# Improve the format of the parsed output.  Set the line wrap as well.
stm_region_parse_pretty() {
	sed --follow-symlinks "s/\(^.*\)/* \1/g" | fold -sw "$stm_fold"
}

# Provide helpful message on the content of the filtered output.
# Whether grep shows or hides content based on a given pattern.
stm_region_feedback() {
	echo -e "Viewing tasks that $1 ${my_green}$stm_region${my_nocol}\n"
}

# Generic error message when the filter options are invoked without
# a second argument.
stm_region_error() {
	echo -e "${my_red}ERROR${my_nocol} A second argument must be supplied"
	echo -e "${my_cyan}stm $1 ${my_nocol} needs a second argument to sort items accordingly"
	echo "Example:"
	echo -e "${my_cyan}stm $1 ${my_magenta} '@myProject'${my_nocol}"
}

# Show contents that match the given pattern
stm_list() {
	if [ -n "$stm_region" ]; then
		stm_region_feedback 'match'
		stm_region_parse | stm_region_parse_pretty
	else
		stm_region_error 'list'
	fi
}

# Hide contents that match the given pattern
stm_omit() {
	if [ -n "$stm_region" ]; then
		stm_region_feedback 'hide'
		stm_region_parse 'v' | stm_region_parse_pretty
	else
		stm_region_error 'omit'
	fi
}

# Conditions
# ----------

# A second argument is required by options that filter the output.
stm_region="$2"

# Display the file's contents in a pretty format when the script is run
# without any arguments.
if [ "$#" == 0 ]; then
	echo "Viewing tasks:"
	grep -nT '^.*' "$task_list" | \
	fold -sw "$stm_fold"
else
	# If there is a first argument, run the appropriate commands.
	case "$1" in
		d|due)
			# TODO can sed do what the grep here does?
			grep -e '=[0-9-]*' "$task_list" | \
			sed --follow-symlinks 's/\(^.*\) =\([0-9-]*\)/\2: \1/g' | \
			sort -g | \
			fold -sw "$stm_fold"
			;;
		e|edit)
			"$EDITOR" "$task_list"
			;;
		l|list)
			stm_list
			;;
		o|omit)
			stm_omit
			;;
		h|help|-h|--help)
			stm_help
			;;
		*)
			echo -e "${my_red}ERROR${my_nocol} ${my_yellow}Not a valid argument.${my_nocol}"
			echo "Try one of these:"
			echo ""
			# Print the help message
			stm_help
			;;
	esac
fi
