"""
    MoinMoin - TaskTable Macro
    Version 0.9

    This macro is the corresponding user interface for the TaskPlanner action.
    
    Usage:
       [[TaskTable]]
       Display tasks for current page
       
       [[TaskTable(page=SomeWikiPage)]]
       Display tasks for page 'SomeWikiPage'

       [[TaskTable(assign=UserName)]]
       Display tasks assigned to 'UserName'
       
       [[TaskTable(page=SomeWikiPage, assign=UserName)]]
       Display tasks assigned to 'UserName' for page 'SomeWikiPage'

       [[TaskTable(sort=status)]]
       [[TaskTable(sort=date)]]
       Display tasks sorted by given criteria

       [[TaskTable(readonly=1, closed=1)]]
       
    args:
       @page=string: display tasks appended to an other page (default=CurrentPage)
       @assign=string: display only tasks for a specific user (default=None)
       @sort=string: sort task by either by status or date (default=sort by prio, then date)
       @closed=bool: display the table in a closed/open state overriding the default cfg value 'task_table_compact'
       @readonly=bool: display the table without the 'remove task' option

    TaskTable is heavily based on AttachTable.
    AttachTable
    @copyright: 2006 Erick Martin
    
    TaskTable
    @copyright: 2007 Oliver Siemoneit
    @license: GNU GPL, see COPYING for details.

"""

import os, random
from MoinMoin import config, wikiutil
from MoinMoin.Page import Page


def _get_tasks(request, pagename, assign):
    tasks = []
    databaseFile = os.path.join(request.cfg.data_dir, "task-db")
    if os.path.isfile(databaseFile):
        for line in open(databaseFile).readlines():
            tmp = line.split("\t")
            # we have a pagename to search for
            if pagename:
                if tmp[12].find(pagename) != -1:
                    # do we also have some restrictions concerning assign?
                    if assign:
                        if tmp[6].find(assign) != -1:
                            tasks.append(tmp)
                    else:
                        tasks.append(tmp)
            # we only have an assign to search for
            elif assign:
                 if tmp[6].find(assign) != -1:
                     tasks.append(tmp)
        return tasks

    return []


def _sort_tasks_default(task1, task2):

    result = _sort_tasks_by_prio(task1, task2)

    # if tasks are not equal, return result
    if result != 0:
        return result
    # tasks are equal, sort them by date
    return _sort_tasks_by_date(task1, task2)


def _sort_tasks_by_prio(task1, task2):
    prio = { 'none'     : 4,
             'low'      : 3,
             'medium'   : 2,
             'high'     : 1,
             'critical' : 0 }

    task1_prio_str = task1[8]
    if task1_prio_str == '':
        task1_prio_str = 'none'
    task1_prio = prio[task1_prio_str]

    task2_prio_str = task2[8]
    if task2_prio_str == '':
        task2_prio_str = 'none'
    task2_prio = prio[task2_prio_str]

    return cmp(task1_prio, task2_prio)


def _sort_tasks_by_date(task1, task2):
    from time import strptime
    time1 = task1[7].strip(' ')
    if time1 != "":
        try:
            time_struct_1 = strptime(time1, "%d.%m.%y")
        except ValueError:
            try:
                time_struct_1 = strptime(time1, "%d.%m.%y %H:%M")
            except ValueError:
                pass # this should never happen since only valid datetime objects are saved in the database
    else:
        return 1

    time2 = task2[7].strip(' ')
    if time2 != "":
        try:
            time_struct_2 = strptime(time2, "%d.%m.%y")
        except ValueError:
            try:
                time_struct_2 = strptime(time2, "%d.%m.%y %H:%M")
            except ValueError:
                pass # this should never happen since only valid datetime objects are saved in the database
    else:
        return -1

    return cmp(time_struct_1, time_struct_2)


def _sort_tasks_by_status(task1, task2):
    status = { 'to do'       : 0,
               'in progress' : 1,
               'pending'     : 2,
               'done'        : 3,
               'failed'      : 4,
               'closed'      : 5,
               'remove me'   : 6 }

    task1_status_str = task1[9]
    task1_status = status[task1_status_str]

    task2_status_str = task2[9]
    task2_status = status[task2_status_str]

    return cmp(task1_status, task2_status)
        

def _table_row(d):
    row = ('   <tr style="display:">\n'
           '      <td> %(data_1)s </td>\n'
           '      <td> %(data_2)s </td>\n'
           '      <td> %(data_3)s </td>\n'
           '      <td> %(data_4)s </td>\n'
           '      <td> %(data_5)s </td>\n'
           '      <td> %(data_6)s </td>\n'
           '      <td> %(data_7)s </td>\n'
           '   </tr>\n') % d
    return row


def file_table(request, pagename, assign, sort_by=None, display_table_closed=0, readonly=0):
    _ = request.getText
    action = 'TaskPlanner'
    page_url = wikiutil.quoteWikinameURL(request.page.page_name)

    # if we have an assign or a page different than the current page forward this information
    # to the task planner user interface
    url_ext_page = url_ext_assign = ""
    if pagename and (request.page.page_name != pagename):
        pagename = wikiutil.escape(pagename, 1)
        pagename = pagename.replace(' ', '%20')
        url_ext_page = "&page=%s" % pagename
    if assign:
        assign = wikiutil.escape(assign, 1)
        assign = assign.replace(' ', '%20')
        url_ext_assign = "&assign=%s" % assign        

    manage_url = '<a href="%(baseurl)s/%(page_url)s?action=%(action)s%(ext1)s%(ext2)s" title="%(title)s" alt="%(title)s">%(text)s</a>' % {
        'baseurl': request.getScriptname(),
        'page_url': page_url,
        'action': action,
        'ext1': url_ext_page,
        'ext2': url_ext_assign,
        'title': _('Add/Manage tasks'),
        'text': _('Add/Manage'), }

    tasks = _get_tasks(request, pagename, assign)

    page_text = assign_text = ""
    if pagename:
        page_text = " " + _("for the '''%(pagename)s''' page") % {'pagename': pagename, }
    if assign:
        assign_text = " " + _("for user '''%(assign)s'''") % {'assign': assign, }

    if not tasks:
        if pagename and Page(request, pagename).exists() == False:
            return _("\n%(icon)s %(manage_url)s tasks %(assign_text)s%(page_text)s %(error)s\n ") % {
                'icon': request.theme.make_icon('table-null'),
                'manage_url': manage_url,
                'assign_text': assign_text,
                'page_text': page_text,
                'error': _("''[No such page]''"), }
        else:
            return _("\n%(icon)s %(manage_url)s tasks %(assign_text)s %(page_text)s \n") % {
                'icon': request.theme.make_icon('table-null'),
                'manage_url': manage_url,
                'assign_text': assign_text,
                'page_text': page_text, }
            

    table_closed_file_name = 'table-open.png'
    table_open_file_name = 'table-close.png'

    open_image = request.theme.img_url(table_open_file_name)
    closed_image = request.theme.img_url(table_closed_file_name)

    javascript_function = """
// Toggle display of a folder's contents.
function showHideContents ( idnum ) {
   the_table = document.getElementById( 't' + idnum );

   arrowObj  = document.getElementById( 'a' + idnum );
   // we use the image to determine if the table is open or closed
   if (arrowObj.src.indexOf('%(table_open_file_name)s') > -1) {
      arrowObj.src = '%(closed_image)s';
      display      = 'none';
   } else {
      arrowObj.src = '%(open_image)s';
      display      = '';
   }

   // change the open/closed state of the rows for the table
   for (var i = 0; i < the_table.rows.length; i++) {
      the_table.rows[i].style.display = display;
   }
}
""" % {'closed_image': closed_image,
       'open_image': open_image,
       'table_open_file_name' : table_open_file_name, }

    html = ("\n"
            '<script type="text/javascript">' "\n"
            "<!--\n"
            "%(javascript_function)s\n"
            "--> </script>\n"
            "\n" ) % { 'javascript_function' : javascript_function, }

    file_id = random.randint(1, 99999999999)

    html = html + '\n<div class="attachmentTable" >\n'

    table_caption1 = ('<a onClick="showHideContents(%(file_id)s);" title="%(alt_title)s">'
                      '<img id="a%(file_id)s" align="middle" border=0 src="%(open_image)s"'
                      'alt="%(alt_title)s"></a>\n') % {
                      'file_id':  file_id,
                      'alt_title': _("Click to open/close table"),
                      'open_image': open_image, }                              
    
    table_caption2 = _("%(manage_url)s tasks%(assign_text)s%(page_text)s ''[%(num_files)s task(s)]''") % {
                         'num_files':  len(tasks),
                         'page_text': page_text,
                         'assign_text': assign_text,
                         'manage_url': manage_url, }

    table_caption = table_caption1 + table_caption2

    html = html + ('<br>\n'
                   '%(table_caption)s\n'
                   '<table id="t%(file_id)s" >\n'
                   '   <tr style="display:">\n'
                   '     <th>%(id)s</th>\n'
                   '     <th>%(name)s</th>\n'
                   '     <th>%(assign)s</th>\n'
                   '     <th>%(priority)s</th>\n'
                   '     <th>%(status)s</th>\n'
                   '     <th>%(date)s</th>\n'
                   '     <th>%(action)s</th>\n'
                   '   </tr>\n') % {
                      'file_id':  file_id,
                      'table_caption':  table_caption,
                      'id': _('ID'),
                      'name': _('Name'),
                      'assign': _('Assign'),
                      'priority': _('Priority'),
                      'status': _('Status'),
                      'date': _('Date'),
                      'action': _('Information'), }

    # if user is not superuser, force readonly mode
    if not request.user.isSuperUser():
        readonly = 1

    # sort tasks
    if sort_by == 'date':
        tasks.sort(_sort_tasks_by_date)
    elif sort_by == 'status':
        tasks.sort(_sort_tasks_by_status)
    else:
        tasks.sort(_sort_tasks_default)


    # output tasks
    for task in tasks:
        # build icon
        task_prio_status = "task-%(prio)s-%(status)s" % {'prio': task[8].lower().replace(' ', ''),
                                                         'status': task[9].lower().replace(' ', ''), }
        if task_prio_status in request.theme.icons:
            image = request.theme.make_icon(task_prio_status)
        else:
            if task[9] == "closed":
                image = request.theme.make_icon('task-closed')
            elif task[9] == "remove me":
                image = request.theme.make_icon('task-removeme')
            else:
                image = request.theme.make_icon('task--inprogress')
                
        # create link to task's homepage
        if task[11].replace(' ', '') != "":
            task_homepage = request.formatter.pagelink(1, task[11]) + request.formatter.text(task[0]) + request.formatter.pagelink(0, task[11])
        else:
            task_homepage = "<strong>%(id)s<strong>" % {'id': task[0], }

        # create 'edit task' link
        ticket = wikiutil.createTicket(request)
        edit_link = '<a class="smallText" href="%(baseurl)s/%(page_url)s?action=TaskPlanner&edit=%(task_id)s&ticket=%(ticket)s">%(text)s</a>' % {
        'baseurl': request.getScriptname(),
        'page_url': page_url,
        'task_id': task[0],
        'ticket': ticket,
        'text': _('[Edit task]'), }

        # create 'remove task' link
        if readonly == 0:
            remove_link = '<a class="smallText" href="%(baseurl)s/%(page_url)s?action=TaskPlanner&remove=%(task_id)s&ticket=%(ticket)s">%(text)s</a>' % {
            'baseurl': request.getScriptname(),
            'page_url': page_url,
            'task_id': task[0],
            'ticket': ticket,
            'text': _('[Remove task]'), }
        else:
            remove_link=""

        data_1 = "<strong>%(image)s %(task_id)s</strong>" % {'image': image, 'task_id': task_homepage, }
        data_2 = "%(task_name)s" % {'task_name': task[5], }
        data_3 = "%(assign)s" % {'assign': task[6], }
        data_4 = "%(priority)s" % {'priority': _(task[8]), }
        data_5 = "%(status)s" % {'status': _(task[9]), }
        data_6 = "%(time_frame)s" % {'time_frame': task[7], }
        data_7 = "%(desc)s <p>" % { 'desc': task[10], }
        if task[3] == 'unknown':
            data_7 += '<p class="smallText"> %(label1)s %(created_by)s %(label2)s</p><p>' % {'label1': _('Task created by'),
                                                                                             'created_by': task[1],
                                                                                             'label2': _('on'), }
            data_7 += '<p class="smallText">%(created_at)s</p><p>' % {'created_at': task[2], }
        else:
            data_7 += '<p class="smallText"> %(label1)s %(closed_by)s %(label2)s</p><p>' % {'label1': _('Task closed by'),
                                                                                            'closed_by': task[3],
                                                                                            'label2': _('on'), }
            data_7 += '<p class="smallText">%(closed_at)s</p><p>' % {'closed_at': task[4], }
        data_7 += '<p class="smallText">%(edit)s %(remove)s</p>' % {'edit': edit_link, 'remove': remove_link, }
    
        html = html + _table_row( { 
                                    'data_1' : data_1,
                                    'data_2' : data_2,
                                    'data_3' : data_3,
                                    'data_4' : data_4,
                                    'data_5' : data_5,
                                    'data_6' : data_6,
                                    'data_7' : data_7,
                                    } )

    html = html + '\n</table>\n</div>\n'

    if display_table_closed:
        close_str = ('\n<script type="text/javascript">\n'
                     '<!--\nshowHideContents(%(file_id)s);\n-->\n'
                     '</script>\n' ) % {'file_id':  file_id, }
        html = html + close_str

    return html


def getArgs(given_arguments, allowed_arguments):
    if not given_arguments:
        return {}
    args = {}
    for s in given_arguments.split(','):
        if s and s.find('=') > 0:
            key, value = s.split('=', 1)
            if key and value:
                key = key.strip()
                if key in allowed_arguments:
                    args[key] = value.strip()
    return args


def execute(macro, options):
    request = macro.request
    
    arguments = ['page', 'assign', 'sort', 'closed', 'readonly']
    args = getArgs(options, arguments)

    assign = args.get('assign', None)

    pagename = args.get('page', None)
    if not pagename and not assign:
        pagename = request.page.page_name

    default_table_closed = 0
    if hasattr(request.cfg, 'task_table_compact'):
        default_table_closed = request.cfg.task_table_compact

    table_closed = int(args.get('closed', default_table_closed))
    table_readonly = int(args.get('readonly', 0))

    sort_by = args.get('sort', None)
    if sort_by not in ['date', 'status']:
        sort_by = None

    return file_table(request, pagename, assign, sort_by, table_closed, table_readonly)

