# -*- 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 renderer import ReportRenderer

class ReportIncludeMacro(Component):
    implements(IWikiMacroProvider, ITemplateProvider)

    # ITemplateProvider methods
    def get_htdocs_dirs(self):
        return [('reportinclude',
                 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 'ReportInclude'

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

args:
 * 表示したいTracReportのIDを指定します。必ず第1引数に指定する必要があります。
 * title: レポート、グラフのタイトル文字列を指定することができます。コロンに続けてタイトル文字列を指定します。未指定の場合は、TracReportのタイトル文字列が自動的に使用されます。
 * graph: グラフも合わせて表示する場合に指定します。コロンに続けて、以下の記載が可能です。
   * lines[[BR]]
     折れ線グラフを表示します。
   * bars[[BR]]
     棒グラフを表示します。
   * pie[[BR]]
     円グラフを表示します。
 * stack: グラフ表示時に、スタック(積み上げ)グラフとするかどうか指定します。コロンに続けて、以下の記載が可能です。
   * true[[BR]]
     スタック(積み上げ)グラフとして表示します。
   * false[[BR]]
     スタック(積み上げ)グラフとして表示しません。
 * barWidth: 棒グラフの幅サイズを指定します。
 * legendLoc: グラフの凡例の表示位置を指定します。コロンに続けて、方角を示す nw, n, ne, e, se, s, sw, w のいずれかを指定します。
 * legendXOffset: グラフの凡例位置の横方向のオフセットを指定します。未指定の場合は12となります。
 * legendYOffset: グラフの凡例位置の縦方向のオフセットを指定します。未指定の場合は12となります。
 * dateFormat: X軸の値を日付としてグラフ表示する場合の、日付フォーマットを指定します。コロンに続けて、yyyy/MM/dd のように指定します。未指定の場合は、デフォルト値としてyyyy-MM-ddを使用します。
 * width: グラフの幅をpx単位で指定します。未指定の場合は536pxとなります。 
 * height: グラフの高さをpx単位で指定します。未指定の場合は300pxとなります。
 * table: TracReportのテーブルを出力を制御できます。コロンに続けて、以下の記載が可能です。
   * hide[[BR]]
     テーブルを表示しません。グラフのみを表示する場合に使用します。
 * async: 非同期でレポート、グラフを描画するかどうか指定します。コロンに続けて、以下の記載が可能です。未指定の場合は、trueとなります。
   * true[[BR]]
     非同期で描画します。
   * false[[BR]]
     非同期で描画しません。
 * xaxisFormatString: X軸のラベルのフォーマットを指定します。X軸が数値か日付によって指定方法が異なります。
   * 数値の場合。未指定の場合は、%.1fとなります。以下のように整数値、少数値の指定が可能です。
     * %d[[BR]]
       整数値で表示します。
     * %.1f[[BR]]
       小数点1桁までで表示します。
   * 日付の場合。未指定の場合は、%Y/%m/%dとなります。
 * yaxisFormatString: Y軸のラベルのフォーマットを指定します。指定可能な値は、上記のX軸の数値と同じです。
 * xaxisMin: X軸の最小値を指定します。指定しない場合は、グラフデータより自動的に計算されます。
 * xaxisMax: X軸の最大値を指定します。指定しない場合は、グラフデータより自動的に計算されます。
 * yaxisMin: Y軸の最小値を指定します。指定しない場合は、グラフデータより自動的に計算されます。
 * yaxisMax: Y軸の最大値を指定します。指定しない場合は、グラフデータより自動的に計算されます。

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

例:
 * report:1 を表示する。
{{{
[[ReportInclude(1)]]
}}}
 * report:9 を棒グラフととともに表示する。
{{{
[[ReportInclude(9,graph:bars)]]
}}}
 * report:14 を折れ線グラフで表示する。テーブルは表示しない。
{{{
[[ReportInclude(14,graph:lines,table:hide)]]
}}}
 * TracReportの動的変数に値を渡して表示する。
{{{
[[ReportInclude(14?PRIORITY=high)]]
}}}
 * TracReportの動的変数にログインユーザ名($USER)を渡して表示する。
{{{
[[ReportInclude(8?USER=$USER)]]
}}}
   * ReportIncludeマクロでは現在TracReportのログインユーザ名($USER)をレポート表示時に自動展開できません。[[BR]]ReportIncludeマクロで表示する際には、動的パラメータとして$USERを渡すようにしてください。
'''

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

    def _add_script(self, req):
        if not hasattr(req, '_reportinclude'):
            #add_stylesheet(req, 'reportinclude/css/reportinclude.css')
            
            # add script and css for jqplot
            add_script(req, 'reportinclude/js/jqplot/excanvas.min.js')
            add_script(req, 'reportinclude/js/jqplot/jquery.jqplot.min.js')
            add_script(req, 'reportinclude/js/jqplot/jqplot.pointLabels.min.js')
            add_script(req, 'reportinclude/js/jqplot/jqplot.barRenderer.min.js')
            add_script(req, 'reportinclude/js/jqplot/jqplot.pieRenderer.min.js')
            add_script(req, 'reportinclude/js/jqplot/jqplot.categoryAxisRenderer.min.js')
            add_script(req, 'reportinclude/js/jqplot/jqplot.dateAxisRenderer.min.js')
            add_script(req, 'reportinclude/js/jqplot/jqplot.cursor.min.js')
            add_script(req, 'reportinclude/js/jqplot/jqplot.highlighter.min.js')
            add_script(req, 'reportinclude/js/jqplot/reportgraph.js')
            add_stylesheet(req, 'reportinclude/css/jqplot/jquery.jqplot.min.css')
            
            req._reportinclude = 0
        else:
            req._reportinclude = req._reportinclude + 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):
        renderer = ReportRenderer(self.env)
        return renderer.render(req, content)
        
    def _render_async(self, req, content):
        index = req._reportinclude
        
        div = tag.div(id="reportgraphtable_async_%d" %(index))
        script = """
jQuery(function(){
  jQuery("#reportgraphtable_loading_%d").ajaxStart(function(){
    jQuery(this).show();
  });
  jQuery("#reportgraphtable_loading_%d").ajaxStop(function(){
    jQuery(this).hide();
  });

  jQuery.ajax({
    type: "POST",
    url: "%s",
    data: "__FORM_TOKEN=%s;index=%s;params=%s",
    dataType: "html",
    success: function (data) {
      jQuery("#reportgraphtable_%d").html(data);
    }
  })
});
""" % (index, index, req.href.tracreportinclude(),
       req.form_token, index, content.replace('&', '__AND__'), index)
        
        div.append(tag.script(script, type='text/javascript'))
        div.append(tag.div((tag.img(src=req.href.chrome('reportinclude/image/loading.gif')),
                            tag.div('loading...')),
                         id='reportgraphtable_loading_%d' %(index),
                         style='border: solid 1px black; top: 50%; left: 50%; width: 50%; height: 200px; padding-top: 100px; text-align: center; top: 50%;'))
        div.append(tag.div('', id='reportgraphtable_%d' %(index)))
        
        return div
