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

from genshi.builder import tag
from genshi.filters import Transformer

from trac.core import TracError
from trac.resource import ResourceNotFound
from trac.ticket.report import ReportModule
from trac.util.datefmt import format_datetime, format_date, format_time, parse_date

class ReportRenderer(object):
    
    def __init__(self, env):
        self.env = env
        
    def render(self, req, params):
        u"""TracReportsの結果をHTMLで返す。オプションによってはテーブル、グラフそれぞれを表示する。
        """
        self.index = req._reportinclude
        
        id, vars, opts = self._parse_params(req, params)
        report = self._get_report_info(id)
        
        if len(report) == 1:
            title, sql, desc = report[0]
            
            if opts['title'] == '':
                opts['title'] = title
                
            table, columns, table_data = self._render_table(req, id, vars, opts, title, sql, desc)
            table = self._render_graph(req, opts, table, columns, table_data, title, desc)
            return table
        else:
            raise ResourceNotFound(
                _('Report [%(num)] does not exist.', num=id),
                _('Invalid Report Number'))
            
    def _render_table(self, req, id, vars, opts, title, sql, desc):
        db = self.env.get_db_cnx()
        
        sql, vars , missing_vars = self._sql_sub_vars(sql, vars, db)
        
        cursor = db.cursor()
        cursor.execute(sql, vars)
        
        if opts['table'] == 'hide':
            table_display = 'none'
        else:
            table_display = 'inline'
        
        table = tag.table(class_="listing tickets",
                          id="reportgraphtable_%d" %(self.index),
                          style="display:%s" %(table_display))
        caption = tag.caption(tag.a('%s(report:%s)' %(title, id), href='%s' % req.href.report(id)),
                              align='center')
        table.append(caption)
        
        tr = tag.tr()
        columns = []
        for desc in cursor.description:
            columns.append(desc[0])
            if desc[0] == '__color__' or desc[0] == '__group__':
                continue
            if desc[0].startswith('_'):
                continue
            
            tr.append(tag.th('%s' % desc[0]))
        
        table.append(tag.thead(tr))
            
        tbody = tag.tbody()
        table.append(tbody)
        
        table_data = {}
        for col in columns:
            table_data[col] = []
        
        for idx, row in enumerate(cursor):
            
            odd_or_even = (idx % 2 == 0) and 'odd' or 'even'
            tr = tag.tr()
            css_class = ''
            
            for col_idx, col in enumerate(row):
                column = columns[col_idx]
                
                if column == '__color__' and css_class is '':
                    css_class = 'color%s-' % col
                    continue
                if column.startswith('_'):
                    continue
                
                converted_col = self._convert_col(req, column, col)
                    
                tr = tr(tag.td(converted_col))
                table_data[column].append(col)
                
            css_class = css_class + odd_or_even
            tr = tr(class_ = css_class)
            tbody.append(tr)
        
        #add_stylesheet(req, 'wikitable/css/wikitable.css')
        return table, columns, table_data

    def _render_graph(self, req, opts, table, columns, table_data, title, desc):
        u"""TracReportsの結果をグラフで表示する。
        """
        if opts['graph'] not in ('lines', 'bars', 'pie'):  
            return table
        
        ticks = table_data[columns[0]]
        
        data = '['
        for column in columns[1:]:
            #print table_data[column]
            values = self._to_js_array(table_data[column])
            data = data + values + ','
        data = data[:-1] + ']'
        
        xaxisType = self._getXAxisType(req, ticks, opts)

        script = """
jQuery(document).ready(function($) {
  $('#reportgraph_%d').reportGraph({
    graph: '%s',
    seriesLabel: %s,
    data: %s,
    ticks: %s,
    title: '%s',
    stack: %s,
    legendLoc: '%s',
    legendXOffset: %s,
    legendYOffset: %s,
    xaxisType: '%s',
    xaxisMin: %s,
    xaxisMax: %s,
    yaxisMin: %s,
    yaxisMax: %s,
    xaxisFormatString: '%s',
    yaxisFormatString: '%s',
    barWidth: '%s'
  });
});
        """ % (self.index, opts['graph'], self._to_js_array(columns[1:]),
               data, self._to_js_array(ticks),
               opts['title'], opts['stack'].lower(), opts['legendLoc'],
               opts['legendXOffset'], opts['legendYOffset'],
               xaxisType, opts['xaxisMin'], opts['xaxisMax'],
               opts['yaxisMin'], opts['yaxisMax'],
               opts['xaxisFormatString'], opts['yaxisFormatString'], opts['barWidth'])
        
        self.env.log.debug(script);

        div = tag.div(
                      tag.div(' ',
                              id="reportgraph_%d" % (self.index),
                              style="width:%spx;height:%spx;" %
                              (opts["width"],opts["height"])),
                      tag.br(),
                      table,
                      tag.script(script, type="text/javascript"),
                      class_="reportgraph",
                      id="reporinclude_%d" % (self.index)
                      )
        return div
    
    def _parse_params(self, req, params):
        opts = {
               'title':'',
               'renderer':'jqplot',
               'width':'536',
               'height':'300',
               'end':'',
               'start':'',
               'per':'day',
               'graph':'',
               'table':'inline',
               'dateFormat':'yyyy-MM-dd',
               'stack':'false',
               'legendLoc':'ne',
               'legendXOffset':'12',
               'legendYOffset':'12',
               'xaxisMin':'null',
               'xaxisMax':'null',
               'yaxisMin':'null',
               'yaxisMax':'null',
               'xaxisFormatString':'',
               'yaxisFormatString':'',
               'barWidth':'50'
               }
        
        vars = {}
        for (index, param) in enumerate(params.split(',')):
            if index == 0:
                id = param
                id, vars = self._parse_vars(req, 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
            opts[key] = value
        return id, vars, opts
    
    def _parse_vars(self, req, 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]
                if vars[entry[0]] == '$USER':
                    vars[entry[0]] = req.authname
        elif params.find('=') != -1:
            entry = params.split('=')
            vars[entry[0]] = entry[1]
            if vars[entry[0]] == '$USER':
                vars[entry[0]] = req.authname
        
        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 _sql_sub_vars(self, sql, vars, db):
        rm = ReportModule(self.env)
        sql, vars, missing_vars = rm.sql_sub_vars(sql, vars, db)
        return sql, vars, missing_vars
    
    def _getXAxisType(self, req, ticks, opts):
        if ticks and len(ticks) > 0:
            try:
                int(ticks[0])
                return 'number'
            except ValueError:
                pass
            
            try:
                parse_date(ticks[0], req.tz)
                return 'date'
            except TracError:
                pass
            
            return 'string'
    
    def _to_js_array(self, array):
        if array is None:
            return 'null'
        if len(array) == 0:
            return '[]'
        
        is_number = True
        for value in array:
            try:
                int(value)
            except ValueError:
                is_number = False
                pass
        
        if is_number:
            return "[" + ",".join(map(str, array)) + "]"
        else:
            return "['" + "','".join(map(str, array)) + "']"
    
    def _convert_col(self, req, column, col):
        if column == 'time' or column.endswith(u'時刻'):
            col = col != None and format_time(int(col)) or '--'
        if column in ('date', 'created', 'modified') or column.endswith(u'日付'):
            col = col != None and format_date(int(col)) or '--'
        if column == 'datetime' or column.endswith(u'日時'):
            col = col != None and format_datetime(int(col)) or '--'
        
        if column.lower() in ('ticket', 'id', u'チケット'):
            col = tag.a('#' + str(col), title='View Ticket', class_='ticket',
                        href=req.href.ticket(str(col)))
        return col
