#!/usr/bin/python
# encoding: utf-8

import cmd, calendar
from etm.etmData import *

year = int(datetime.date.today().strftime("%Y"))
beg_year = year - year_beg
end_year = year + year_end
data_beg = (beg_year, 1, 1)
data_end = (end_year, 12, 31)
if sundayfirst:
    week_begin = 6
else:
    week_begin = 0
# hack to set locale on both darwin and linux
if mac:
    lcl = locale.getdefaultlocale()
    cal = calendar.LocaleTextCalendar(week_begin, lcl[0])
elif windoz:
    locale.setlocale(locale.LC_ALL, '')
    lcl = locale.getlocale()
    cal = calendar.LocaleTextCalendar(week_begin, lcl)
else:
    lcl = locale.getdefaultlocale()
    cal = calendar.LocaleTextCalendar(week_begin, lcl)

all_off, attrs, codes = get_attrs()


class IntOrCmd(cmd.Cmd):
    """Accepts commands via the normal interactive
    prompt or on the command line.
    """
    doc_header = """documented etm commands
Enter '? X' (or 'help X') for
details about command X"""
    misc_header = 'miscellaneous etm commands'
    undoc_header = 'undocumented etm commands'
    num2id = {}
    num2file = {}
    curnum = (0, '')
    common_prefix = ''
    all_tups = []
    alerts = []
    busytimes = []
    id2hash = {}
    lastmodified = ''
    currenthash = {}
    contexts = []
    locations = []
    msgs = []
    #  common_prefix, all_tups, alerts, busytimes, id2hash, lastmodified,\
            #  currenthash, contexts, keywords, locations, msgs = \
            #  getTuples(data_beg, data_end)
    data_loaded = False
    show_nums = show_nums
    use_colors = use_colors
    options = {}
    y,m,d = map(int, datetime.date.today().strftime("%Y:%m:%d").split(':'))
    options['begin_date'] = (y,m,d)

    def loadData(self, force=False):
        if self.data_loaded:
            if force:
                print "working ...",
            else:
                return()
        tup = getTuples(data_beg, data_end)
        self.common_prefix = tup[0]
        self.all_tups = tup[1]
        self.alerts = tup[2]
        self.id2hash = tup[3]
        self.lastmodified = tup[4]
        self.currenthash = tup[5]
        self.contexts = tup[6]
        self.keywords = tup[7]
        self.locations = tup[8]
        self.msgs = tup[9]
        if self.msgs:
            print "Errors loading data"
            for m in self.msgs:
                print m
        if 'begin_date' in self.options:
            d = "-b %d-%02d-%02d" % (self.options['begin_date'])
        else:
            d = datetime.date.today().strftime("-b %Y-%m-%d")
        if self.data_loaded:
            print "done"
            self.do_o(d)
        else:
            self.data_loaded = True

    def preloop(self):
        self.prompt = "enter 'q' to quit or '?' (or 'help') for usage information\netm: "

    def getFile(self, line):
        if not line or line[0] not in ['~', '*', '!', '-', '+']:
            print "no entry"
            return()
        mode = char2Mode[line[0]]
        self.common_prefix, filetups = getFiles()
        curfile = relpath(self.currenthash[mode], self.common_prefix)
        self.num2file = {}
        num = 0
        for f, r in filetups:
            num += 1
            if r == curfile:
                self.curnum = (num, r)
            self.num2file[num] = f
            print "%2d] %s" % (num, r)
        print "Enter 0 to cancel, an empty string to accept the default:"
        print "    %2d] %s" % (self.curnum[0], self.curnum[1])
        print "or the number of the file to use"
        confirm = raw_input('filenumber [%d] ' % self.curnum[0])
        if confirm == "":
            return(self.num2file[self.curnum[0]])
        else:
            try:
                n = int(confirm)
            except:
                print "an integer must be entered"
                return()
            if n <= 0:
                print "cancelled"
                return()
            elif n in self.num2file:
                return(self.num2file[n])
            else:
                print "invalid file number"
                return()

    def do_numbers(self, line):
        self.show_nums = not self.show_nums
        if self.show_nums:
            print "show item numbers is on"
        else:
            print "show item numbers is off"

    def do_colors(self, line):
        self.use_colors = not self.use_colors
        if self.use_colors:
            print "use colors is on"
        else:
            print "use colors is off"

    def do_c(self, line):
        if line:
            dt = parse_date(line)
            y1, m1, d1 = map(int, dt.strftime("%Y:%m:%d").split(":"))
        else:
            y1, m1, d1 = self.options['begin_date']
        # get the previous, current and subsequent year months
        if m1 == 1:
            m0 = 12
            y0 = y1 - 1
            m2 = 2
            y2 = y1
        elif m1 == 12:
            m0 = 11
            y0 = y1
            m2 = 1
            y2 = y1 + 1
        else:
            y0 = y1
            y2 = y1
            m0 = m1 - 1
            m2 = m1 + 1
        mon0 = cal.formatmonth(y0, m0).split('\n')
        mon1 = cal.formatmonth(y1, m1).split('\n')
        mon2 = cal.formatmonth(y2, m2).split('\n')
        mlen = max(len(mon0), len(mon1), len(mon2))
        for l in [mon0, mon1, mon2]:
            while len(l) < mlen:
                l.append('')
        i = 0
        c = []
        while i < mlen:
            c.append("  %-20s    %-20s    %-20s" % (mon0[i], mon1[i], mon2[i]))
            i += 1
        print '\n'.join(c)

    def do_r(self, line):
        self.loadData(force=True)

    def do_b(self, line):
        self.loadData()
        all_off, attrs, codes = get_attrs()
        opt_lst = []
        tmp = re.split('(?<!\S)-(?!\d)', line.decode(term_encoding))
        p = tmp.pop(0)
        for x in tmp:
            if not x:
                continue
            opt_lst.extend(["-%s" % x[0], ("%s" % x[1:]).strip()])
        self.options = get_opts(nameHash['b'], opt_lst)
        parse_opts(self.options)
        lst = data2Busy(self.busytimes, self.options)
        for l in lst:
            if len(l) >=3:
                a = codes[l[2]]
            else:
                a = ''
            if self.use_colors:
                print "%s%s%s" % (a, l[0], all_off)
            else:
                print "%s" % (l[0])
        print ''

    def __editStr(self, s, quitIfUnchanged=True):
        temp = os.path.join(etmdir, ".temp.txt")
        fo = open(temp, 'w')
        fo.writelines(s)
        fo.close()
        lastmod =  os.path.getmtime(temp)
        hsh = {
                'e' : term_editor,
                'f' : temp,
                'n' : 1
                }
        cmd = term_editcmd % hsh
        retcode = subprocess.call(cmd, shell=True)
        if quitIfUnchanged:
            curmod =  os.path.getmtime(temp)
            if curmod == lastmod :
                print "unchanged\n"
                return()
        loop = True
        while loop:
            fo = open(temp, 'r')
            lines = fo.readlines()
            fo.close()
            s = " \n".join(lines)
            msg, hsh, s = checkLines(s)
            if not msg:
                return(s)

            for m in msg:
                print m
            confirm = raw_input('Correct entry? [Yn] ')
            if confirm.upper() == 'N':
                return()
            retcode = subprocess.call(cmd, shell=True)

    def do_n(self, line):
        if 'begin_date' in self.options:
            d = "@d %d-%02d-%02d" % (self.options['begin_date'])
        else:
            d = datetime.date.today().strftime("@d %Y-%m-%d")
        if line and line[0] in ['~', '*', '!', '-', '+']:
            s = "%s %s" % (line[0], d)
        else:
            s = '   %s' % d
        s = self.__editStr(s)
        if s:
            fp = self.getFile(s)
            linesAdd(s, fp)
            self.loadData(force=True)
            return()

    def do_d(self, line):
        try:
            num = int(line)
            idstr = self.num2id[num]
        except:
            print "could not find id corresponding to '%s'" % line
            return False
        f, n, m = idstr.split(':')
        fp = os.path.join(self.common_prefix, f)
        lines = linesGet(int(n), int(m), fp)
        print "-"*60
        print lines
        print "-"*60
        confirm = raw_input('Delete this item? [yN] ')
        if confirm.upper() == 'Y':
            linesDelete(n, m, fp)
            self.loadData(force=True)

    def do_e(self, line):
        try:
            num = int(line)
            idstr = self.num2id[num]
        except:
            print "could not find id corresponding to '%s'" % line
            return False
        f, n, m = idstr.split(':')
        fp = os.path.join(self.common_prefix, f)
        lines = linesGet(int(n), int(m), fp)
        s = self.__editStr(lines)
        if s:
            linesReplace(n, m, s, fp)
            self.loadData(force=True)
            return()

    def do_f(self, line):
        parts = line.split(' ')
        date = datetime.datetime.today().strftime("%Y-%m-%d")
        try:
            num = int(parts[0])
            idstr = self.num2id[num]
        except:
            print "could not find id corresponding to '%s'" % line
            return()
        f, n, m = idstr.split(':')
        fp = os.path.join(self.common_prefix, f)
        lines = linesGet(int(n), int(m), fp)
        if len(parts) > 1:
            try:
                dstr = " ".join(parts[1:])
                date = duparse(dstr).strftime("%Y-%m-%d")
            except:
                print "Could not parse '%s'. Using '%s'" % (dstr, date)
        if not idstr in self.id2hash:
            print "invalid id '%s'" % idstr
            return()

        item = self.id2hash[idstr]
        if item[u'leader'][0] not in ['-', '+']:
            print 'only tasks can be finished'
            return()
        tmp = {}
        for key in item:
            tmp[key] = item[key]
        if u'_f' in tmp:
            if 'r' in tmp:
                lst = list(tmp[u'_f'])
                tup = map(int, date.split('-'))
                lst.append(tup)
                tmp[u'_f'] = tuple(lst)
                slst = ["%s-%s-%s" % (x[0],x[1],x[2]) for x in lst]
                tmp[u'f'] = "(%s)" % ', '.join(slst)
            else:
                self.infoMessage(
                    'this non repeating task is already finished', 4)
                return()
        else:
            if 'r' in tmp:
                tmp[u'f'] = "(%s)" % date
            else:
                tmp[u'f'] = "%s" % date
        s = hash2Str(tmp)
        s = self.__editStr(s, quitIfUnchanged = False)
        if s:
            linesReplace(n, m, s, fp)
            self.loadData(force=True)

    def do_m(self, line):
        try:
            num = int(line)
            idstr = self.num2id[num]
        except:
            print "could not find id corresponding to '%s'" % line
            return False
        f, n, m = idstr.split(':')
        fp = os.path.join(self.common_prefix, f)
        lines = linesGet(int(n), int(m), fp)
        print "-"*60
        print lines
        print "-"*60
        confirm = raw_input('Move this item to a different file? [yN] ')
        if confirm.upper() == 'Y':
            np = self.getFile(lines)
            linesAdd(lines, np)
            linesDelete(n, m, fp)
            self.loadData(force=True)
            return()

    def do_u(self, line):
        try:
            num = int(line)
            idstr = self.num2id[num]
        except:
            print "could not find id corresponding to '%s'" % line
            return False
        f, n, m = idstr.split(':')
        fp = os.path.join(self.common_prefix, f)
        s = ''
        if idstr in self.id2hash:
            item = self.id2hash[idstr]
            if item[u'leader'][0] not in ['-', '+']:
                print 'only tasks can be unfinished'
                return()
            tmp = {}
            for key in item:
                tmp[key] = item[key]
            if u'_f' in tmp:
                if len(tmp[u'_f']) > 1:
                    tmp[u'_f'] = item[u'_f'][:-1]
                    lst = list(tmp[u'_f'])
                    tmp[u'_f'] = tuple(lst)
                    slst = ["%s-%s-%s" % (x[0],x[1],x[2]) for x in lst]
                    tmp[u'f'] = "(%s)" % ', '.join(slst)
                else:
                    del tmp[u'_f']
                    del tmp[u'f']
                s = hash2Str(tmp)
                s = self.__editStr(s, quitIfUnchanged = False)
                if s:
                    linesReplace(n, m, s, fp)
                    self.loadData(force=True)
            else:
                print 'there are no finish dates to remove'

    def do_j(self, line):
        self.loadData()
        line = "-b %s" % line.decode(term_encoding)
        self.do_o(line, c='j')

    def do_o(self, line, c='o'):
        self.loadData()
        self.num2id = {}
        opt_lst = []
        tmp = re.split('(?<!\S)-(?!\d)', line.decode(term_encoding))
        p = tmp.pop(0)
        for x in tmp:
            if not x:
                continue
            opt_lst.extend(["-%s" % x[0], ("%s" % x[1:]).strip()])
        self.options = get_opts(nameHash[c], opt_lst)
        parse_opts(self.options)
        if 'cols' in self.options and 'F' in self.options['cols']:
            h, data, self.options = make_tree(self.all_tups, self.options, self.id2hash)
            dt = getChildren(data, level=-1, dt=[], prefix=25,
                    totalsfirst=self.options['totalsfirst'],
                    details=self.options['details'],
                    id2hash=self.id2hash)
            while dt and not dt[-1][0]:
                dt.pop(-1)
        else:
            dt = data2Report(self.all_tups, self.options, self.id2hash)
        num = 0
        for s, f, a, i in dt:
            if a <= 10 and self.show_nums:
                num += 1
                if id:
                    self.num2id[num] = i
                if self.use_colors:
                    snum = " %s[%d]%s" % (attrs[14],num, all_off)
                else:
                    snum = " [%d]" % (num)
            else:
                snum = ""
            if self.use_colors:
                term_print("%s%s%s%s" % (attrs[a], s, all_off, snum))
            else:
                term_print("%s%s" % (s, snum))
        #  for s, f, a in dt:
            #  print attrs[a], s, all_off
        if 'vcal' in self.options and self.options['vcal']:
            ret, s = make_vcal('export.ics', self.all_tups, self.options)
            term_print("\n%s" % s)
        if 'values' in self.options and self.options['values']:
            s = make_csv('export.csv', self.all_tups, self.options)
            term_print("\n%s" % s)
        print ''

    def do_q(self, line):
        return True

    def do_v(self, line):
        print "%s" % newer()[1]

    def do_w(self, line):
        try:
            import etm.etmWX
            etm.etmWX.main()
        except:
            print('Errors importing etm.etmWX ')
            print(sys.exc_info())

    def busy_help(self):
        parser = ETMOptParser(usage = '')
        for opt in parserOpts[nameHash['b']]:
            s, a, dst, dflt, hlp = optionParms[opt]
            if dflt:
                parser.add_option(s, action = a, dest = dst, default = dflt,
                        help = hlp)
            else:
                    parser.add_option(s, action = a, dest = dst,  help = hlp)
        parser.print_help()

    def help_b(self):
        print "Display busy report using provided self.options."
        print "b [self.options]"
        self.busy_help()

    def outline_help(self):
        parser = ETMOptParser(usage = '')
        for opt in parserOpts[nameHash['o']]:
            s, a, dst, dflt, hlp = optionParms[opt]
            if dflt:
                parser.add_option(s, action = a, dest = dst, default = dflt,
                        help = hlp)
            else:
                    parser.add_option(s, action = a, dest = dst,  help = hlp)
        parser.print_help()

    def help_o(self):
        print """\
Usage: o [options]
Display outline report using provided options."""
        self.outline_help()

    def help_colors(self):
        print "Toggle the use of colors in the display."

    def help_c(self):
        print """\
Usage: c [D]
Display a three month calendar spanning the (fuzzy parsed) date, D,
or, if D is not provided, the current 'begin_date'."""

    def help_numbers(self):
        print "Toggle the display of item numbers."

    def help_r(self):
        print "Reload the etm data files."

    def help_n(self):
        print """\
Create a new item using
    %s
(the current setting for 'term_editor' in %s).
The new item will be appended to a file to be selected after closing the editor.""" % (term_editor, etmrc)

    def help_d(self):
        print """\
Usage: d N
Delete the item corresponding to the provided item number, N."""

    def help_e(self):
        print """\
Usage: e N
Open the item corresponding to the provided item number, N, using
    %s
(the current setting for 'term_editor' in %s).""" % (term_editor, etmrc)

    def help_f(self):
        print """\
Usage: f N [D]
Mark finished the task corresponding to the provided item number, N, using
the provided date, D, (fuzzy parsed). The current date is used if no date
is provided."""

    def help_j(self):
        print """\
Usage: j D
Jump to the date, D (fuzzy parsed). Relative dates are supported so that, e.g.,
'j -21' would jump to the date that is three weeks before today."""

    def help_m(self):
        print """\
Usage: m N
Move the item corresponding to the provided item number, N, to a new file."""

    def help_u(self):
        print """\
Usage: u N
Unfinish the task corresponding to the provided item number, N, by removiding
the last finish date."""

    def help_v(self):
        print "Check for a newer version of etm."

    def help_w(self):
        print """\
Start the GUI version of etm.
Enter this option at the command line to avoid blocking the interactive loop."""

    def help_help(self):
        print "Usage: X [options]"
        print "where the command X is either b, B, o, O, n or w."
        print "Using lower/upper case gives output with/without colors."
        print "Use 'help X' for detailed help on command X."

    def help_q(self):
        print "quit"

if __name__ == '__main__':
    import sys
    if len(sys.argv) > 1:
        c = IntOrCmd()
        c.show_nums = False
        if sys.argv[1] in ['B', 'O']:
            c.use_colors = False
            sys.argv[1] = sys.argv[1].lower()
        c.onecmd(' '.join(sys.argv[1:]))
    else:
        IntOrCmd().cmdloop()
