# -*- coding: utf-8 -*-

import pkg_resources
import re

from genshi.builder import tag

from trac.core import *
from trac.config import Option
from trac.util import TracError
from trac.util.datefmt import format_datetime, format_date, format_time
from trac.util.html import Markup
from trac.util.text import to_unicode
from trac.util.translation import _
from trac.mimeview.api import Mimeview, get_mimetype, Context, WikiTextRenderer
from trac.resource import ResourceNotFound
from trac.ticket.report import ReportModule
from trac.web.api import ITemplateStreamFilter
from trac.web.chrome import add_link, add_script, add_stylesheet, ITemplateProvider
from trac.wiki.api import IWikiMacroProvider
from trac.wiki.model import WikiPage

from util import *

class MailArchiveReportMacro(Component):
    implements(IWikiMacroProvider, ITemplateProvider)
    
    # ITemplateProvider methods
    def get_htdocs_dirs(self):
        return [('mailarchive',
                 pkg_resources.resource_filename(__name__, 'htdocs'))]

    def get_templates_dirs(self):
        return [pkg_resources.resource_filename(__name__, 'templates')]
    
    # IWikiMacroProvider methods
    def get_macros(self):
        yield 'MailArchiveReport'

    def get_macro_description(self, name):
        return u'''
以下のようにWikiにマクロを記載することで、TracReportで定義されているレポートを表示します。
また、オプションを設定することで、レポートの表からグラフを生成することもできます。
{{{
[[ReportInclude(args1,args2,...)]]
}}}

args:
 * 表示したいTracReportのIDを指定します。必ず第1引数に指定する必要があります。
 * title: レポート、グラフのタイトル文字列を指定することができます。コロンに続けてタイトル文字列を指定します。未指定の場合は、TracReportのタイトル文字列が自動的に使用されます。
 * async: 非同期でレポート、グラフを描画するかどうか指定します。コロンに続けて、以下の記載が可能です。未指定の場合は、trueとなります。
   * true[[BR]]
     非同期で描画します。
   * false[[BR]]
     非同期で描画しません。

表からグラフの生成は、以下のルールに従って行われます。
 * 一番左の列が、X軸の値になります。
  * デフォルトではyyyy-MM-dd形式の場合は日付と見なして、時系列データとして扱います。
 * 2列目以降がグラフのデータとなります。
 * ヘッダ行の値がラベルになります。

例:
 * report:1 を表示する。
{{{
[[ReportInclude(1)]]
}}}
 * report:9 を棒グラフととともに表示する。
{{{
[[ReportInclude(9,graph:bars)]]
}}}
 * report:14 を折れ線グラフで表示する。テーブルは表示しない。
{{{
[[ReportInclude(14,graph:lines,table:hide)]]
}}}
'''

    def expand_macro(self, formatter, name, content):
        
        if not content:
            raise ResourceNotFound(u"[[MailArchiveReport(1)]]のように引数を指定してください。") 
        req = formatter.req
        
        self._add_script(req)
        
        if self._is_async(content):
            return self._render(req, content)
        else:
            return self._render(req, content)

    def _add_script(self, req):
        if not hasattr(req, '_mailarchivereportmacro'):
            add_script(req, 'mailarchive/js/mailarchive.js')
            add_stylesheet(req, 'common/css/report.css')
            add_stylesheet(req, 'mailarchive/css/mailarchive.css')
            add_stylesheet(req, 'mailarchive/css/mailarchivereport.css')
            req._mailarchivereport = 0
        else:
            req._mailarchivereport = req._mailarchivereport + 1
            
    def _is_async(self, content):
        match = re.match(r'.*(async\s*:\s*false).*', content.lower())
        if match:
            return False
        # default Ajax Mode
        return True
            
    def _render(self, req, content):
        u"""TracReportsの結果をHTMLで返す。
        """
        self.index = req._mailarchivereport
        
        id, vars, opts = self._parse_params(content)
        report = self._get_report_info(id)
        
        if len(report) == 1:
            title, sql, desc = report[0]
            
            if opts['title'] == '':
                opts['title'] = title
                
            return self._render_table(req, id, vars, opts, title, sql, desc)
        else:
            raise ResourceNotFound(
                _('Report [%(num)] does not exist.', num=id),
                _('Invalid Report Number'))
    
    def _parse_params(self, params):
        default_opts = {
           'title':'',
           }
        
        vars = {}
        for (index, param) in enumerate(params.split(',')):
            if index == 0:
                id = param
                id, vars = self._parse_vars(id)
                continue
            
            colon_split = param.split(':')
            key = colon_split[0].strip()
            value = ''
            if len(colon_split) > 1:
                value = ':'.join(colon_split[1:])
            else:
                value = True
            default_opts[key] = value
        return id, vars, default_opts
        
    def _parse_vars(self, id):
        vars = {}
        
        if id.find('?') == -1:
            return id, vars
        
        id_and_params = id.split('?')
        params = id_and_params[1]
        id = id_and_params[0]
        
        if params.find('&') != -1:
            for (index, param) in enumerate(params.split('&')):
                if param.find('=') == -1:
                    continue
                entry = param.split('=')
                vars[entry[0]] = entry[1]
        elif params.find('=') != -1:
            entry = params.split('=')
            vars[entry[0]] = entry[1]
        
        return id, vars
    
    def _get_report_info(self, id):
        db = self.env.get_db_cnx()
        cursor = db.cursor()
        cursor.execute("SELECT title,query,description from report "
                       "WHERE id=%s", (id,))
        
        report = [(title, sql, desc) for title, sql, desc in cursor]
        return report
    
    def _render_table(self, req, id, vars, opts, title, sql, desc):
        db = self.env.get_db_cnx()
        
        sql, vars = self._sql_sub_vars(sql, vars, db)
        
        cursor = db.cursor()
        cursor.execute(sql, vars)
        
        columns = []
        g_idx = None
        c_idx = None
        ticket_idx = None
        hidden_idxs = []
        for idx, col in enumerate(cursor.description):
            columns.append(col[0])
            if col[0] == '__group__':
                g_idx = idx
                hidden_idxs.append(idx)
            if col[0] == '__color__':
                c_idx = idx
                hidden_idxs.append(idx)
            if col[0].startswith('_'):
                hidden_idxs.append(idx)
            if col[0].lower().strip() in ('id', 'ticket', u'チケット'):
                ticket_idx = idx
            
        rows = []
        groups = []
        current_group = None
        ticket_ids = []
        for idx, record in enumerate(cursor):
            if g_idx is not None:
                group = record[g_idx]
                if current_group and current_group['name'] != group:
                    current_group['end'] = idx - 1
                    groups.append(current_group)
                    current_group = None
                    
                if not current_group:
                    current_group = {'name':group, 'start':idx}
            
            row = []
            for col_idx, data in enumerate(record):
                row.append(data)
                
            ticket_id = None
            if ticket_idx is not None:
                ticket_id = record[ticket_idx]
                ticket_ids.append(ticket_id)
            rows.append((row, ticket_id))
        
        if current_group:
            current_group['end'] = len(rows) - 1
            groups.append(current_group)
            
            
        mail_ids = self._get_related_mails(db, ticket_ids)
            
        def is_hidden(col_idx):
            return col_idx in hidden_idxs
        
        def has_mail_ids(ticket_id):
            mids = mail_ids.get(ticket_id, [])
            if len(mids) > 0:
                return True
            else:
                return False
            
        data = {}
        data['req'] = req
        data['id'] = atomic_id()
        data['report_id'] = id
        data['title'] = title
        data['has_group'] = g_idx != None
        data['g_idx'] = g_idx
        data['groups'] = groups
        data['c_idx'] = c_idx
        data['columns'] = columns
        data['rows'] = rows
        data['mail_ids'] = mail_ids
        data['format_time'] = format_time
        data['format_datetime'] = format_datetime
        data['is_hidden'] = is_hidden
        data['has_mail_ids'] = has_mail_ids
        
        table = template(self.env, 'mail_ticketreport.html', data)
        
        return table
    
    def _get_related_mails(self, db, ticket_ids):
        if not ticket_ids or len(ticket_ids) == 0:
            return {}
        
        sql = """
        SELECT t.id, tc1.value FROM ticket t LEFT OUTER JOIN ticket_custom tc1 ON tc1.ticket = t.id AND tc1.name = 'mail_id' WHERE t.id IN (%s)
        """ % ('%s,' * len(ticket_ids))[:-1]
        
        cursor = db.cursor()
        cursor.execute(sql, ticket_ids)
        
        mails = {}
        for ticket_id, mail_id in cursor:
            mail_ids = [x.strip() for x in mail_id.split(',') if x != '']
            mails[ticket_id] = mail_ids
        return mails
    
    def _convert(self, column, data):
        if column == 'time':
            data = data != None and format_time(int(data)) or '--'
        if column in ('date', 'created', 'modified'):
            data = data != None and format_date(int(data)) or '--'
        if column == 'datetime':
            data = data != None and format_datetime(int(data)) or '--'
        if column.lower() in ('ticket', 'id', u'チケット'):
            data = tag.a('#' + str(col), title='View Ticket', class_='ticket',
                        href=req.href.ticket(str(col)))
    
    def _sql_sub_vars(self, sql, vars, db):
        rm = ReportModule(self.env)
        sql, vars = rm.sql_sub_vars(sql, vars, db)
        return sql, vars