#!/bin/sh
# encoding: utf-8
from __future__ import print_function
''''which python2 >/dev/null 2>&1 && exec python2 "$0" "$@" # '''
''''which python  >/dev/null 2>&1 && exec python  "$0" "$@" # '''
''''exec echo "Error: I can't find python anywhere"         # '''
# Author: Tim Pulver
# Date: 2015
# https://github.com/timpulver/todo.txt-graph
#
# Visualizes the completed tasks in todo.txt with horizontal bar graphs
# Items are coloured green when there are more than 5 completed tasks per day,
# grey otherwise to motivate completing stuff.
# Have a look at Jerry Seinfeld's "Don't break the chain" methodoligy:
# https://dontbreakthechain.com/what
# You can change the threshold by adding this line to your todo.cfg file:
# export TODOTXT_GRAPH_THRESHOLD=123
#
# Params:
#   - TODO_TXT_PATH: Where the todo.txt / done.txt file is located
#   - NUMBER_OF_DAYS: How many days should be visualized (optional, default: 7)

import datetime
import sys
import os
import re
import collections

TICK_CHAR = '■'
DEFAULT_THRESHOLD = 5 # grey bar < DEFAULT_THRESHOLD >= green bar
WIDTH = 60 # Graph width
DONE = "done.txt"
TODO = "todo.txt"

# try to use xrange, which is faster
try:
    range = xrange
except NameError:
    pass



# Bash color codes and color helper class from:
# https://github.com/emilerl/emilerl/blob/master/pybash/bash/__init__.py
RESET = '\033[0m'
CCODES = {
    'black'           :'\033[0;30m',
    'blue'            :'\033[0;34m',
    'green'           :'\033[0;32m',
    'cyan'            :'\033[0;36m',
    'red'             :'\033[0;31m',
    'purple'          :'\033[0;35m',
    'brown'           :'\033[0;33m',
    'light_gray'      :'\033[0;37m',
    'dark_gray'       :'\033[0;30m',
    'light_blue'      :'\033[0;34m',
    'light_green'     :'\033[0;32m',
    'light_cyan'      :'\033[0;36m',
    'light_red'       :'\033[0;31m',
    'light_purple'    :'\033[0;35m',
    'yellow'          :'\033[0;33m',
    'white'           :'\033[0;37m',
}

# A helper class to colorize strings
class Colors(object):
    def __init__(self, state = False):
        self.disabled = state

    def disable(self):
        self.disabled = True

    def enable(self):
        self.disabled = False

    def __getattr__(self,key):
        if key not in CCODES.keys():
            raise AttributeError("Colors object has no attribute '%s'" % key)
        else:
            if self.disabled:
                return lambda x: x
            else:
                return lambda x: RESET + CCODES[key] + x + RESET

    def __dir__(self):
        return self.__class__.__dict__.keys() + CCODES.keys()



# Graph based on:
# https://github.com/mkaz/termgraph/blob/master/termgraph.py
def print_blocks(label, count, step):
    # when nothing has been done, step equals 0 so the number
    # of blocks/ticks can't be computed but also doesn't
    # matter, hence set blocks = 0 in that case.
    blocks = int(count / step) if step > 0 else 0
    print("{}: ".format(label), end="")
    threshold = int(os.getenv('TODOTXT_GRAPH_THRESHOLD', DEFAULT_THRESHOLD))

    c = Colors()

    for i in range(blocks):
        if count >= threshold:
            sys.stdout.write(c.green(TICK_CHAR))
        else:
            sys.stdout.write(c.light_gray(TICK_CHAR))

    # format the text (number of tasks done)
    if count < 10:
        print("{:>2.0f}".format(count))
    elif count < 100:
        print("{:>3.0f}".format(count))
    else:
        print("{:>4.0f}".format(count))

# Initialize dictionary with all days to also display days
# where no tasks were completed
def initialize_dic(cutoffDays = 7):
    base = datetime.datetime.today().date()
    dic = {(base - datetime.timedelta(days=x)).isoformat() : 0
           for x in range(0, cutoffDays)}
    return dic

# Based on Lately Addon:
# https://github.com/emilerl/emilerl/tree/master/todo.actions.d
def main(todo_file, done_file, cutoffDays = 7):
    lines = []
    files = [todo_file, done_file]
    for filename in files:
        with open(filename, 'r') as f:
            lines.extend(f.readlines())
    today = datetime.datetime.today()
    cutoff =  today - datetime.timedelta(days=cutoffDays)
    dic = initialize_dic(cutoffDays)

    for line in lines:
        m = re.match("x ([\d]{4}-[\d]{2}-[\d]{2}).*", line)
        if m is not None:
            done = m.group(1)
            year, month, day = m.group(1).split("-")
            completed = datetime.datetime(int(year),int(month),int(day))
            if completed >= cutoff:
                #print c.green(m.group(1)) + " " + c.blue(line.replace("x %s" % m.group(1), "").strip())
                if m.group(1) in dic:
                    oldVal = dic.get(m.group(1), 1)
                    dic.update({m.group(1): oldVal+1})
                else:
                    dic[m.group(1)] = 1

    # find out max value
    max = 0
    for key, value in dic.items():
        if value > max:
            max = value

    maxf = float(max)
    step = maxf / WIDTH

    orderedDic = collections.OrderedDict(sorted(dic.items()))

    # display graph
    print()
    for key, value in orderedDic.items():
        print_blocks(key, value, step)
    print()


if __name__ == '__main__':
    if len(sys.argv) < 3:
        print("Usage: graph.py [TODO_FILE] [DONE_FILE] <days back>")
        sys.exit(1)

    if os.path.isfile(sys.argv[1]) and os.path.isfile(sys.argv[2]):
        if len(sys.argv) == 4: 
            main(sys.argv[1], sys.argv[2], int(sys.argv[3]))
        else:
            main(sys.argv[1], sys.argv[2])
    else:
        print("Error: %s or %s doesn't exist" % (sys.argv[1], sys.argv[2]))
        sys.exit(1)