# -*- coding: utf-8 -*-
import re
import time
from datetime import datetime
from pkg_resources import resource_filename
from trac.log import logger_factory
from trac.config import BoolOption
from trac.core import *
from trac.util import Markup
from trac.util.datefmt import utc, to_utimestamp
from trac.web import IRequestHandler
from trac.web.href import Href
from trac.ticket import Ticket
from trac.web.chrome import add_stylesheet, add_script, add_warning,\
     INavigationContributor, ITemplateProvider
from timingandestimationplugin.reportmanager import CustomReportManager
from timingandestimationplugin.statuses import get_statuses
from timingandestimationplugin import reports
from tracsteinschart.api import _, tag_, N_, add_domain

class WorktimeRecorder(Component):
    implements(INavigationContributor, IRequestHandler, ITemplateProvider)
    
    show_parent_ticket = BoolOption('steinschart', 'show_parent_ticket', 'false', """Show parent ticket (needs subtickets plugin).(default: false)""")

    def __init__(self):
        locale_dir = resource_filename('tracsteinschart', 'locale')
        add_domain(self.env.path, locale_dir)
        pass

    # INavigationContributor methods
    def get_active_navigation_item(self, req):
        val = re.search('/worktime$', req.path_info)
        if val and val.start() == 0:
            return "worktime"
        else:
            return ""

    def get_navigation_items(self, req):
        url = req.href.worktime()
        if req.perm.has_permission("TICKET_MODIFY"):
            yield 'mainnav', "worktime", \
                  Markup('<a href="%s">%s</a>' % \
                         (req.href('worktime') , _(u'作業時間入力')))

    def process_request(self, req):
        db = self.env.get_db_cnx()
        cursor = db.cursor()

        errors = []
        if req.method == 'POST':
            params = {}
            for param in req.args.keys():
                try:
                    property, id = param.split('_')
                    p = None
                    try:
                        p = params[id]
                    except KeyError:
                        p = params[id] = {}
                    p[property]=req.args.get(param)
                    if p[property]=='':
                        continue
                    if property=='totalhours':
                        try:
                            p[property]=str(p[property])
                            float(p[property])
                        except ValueError:
                            errors.append({'id': id , 'name': 'totalhours', 'value': p[property]})
                            add_warning(req,"チケット#%sの合計時間%sが実数ではありません。" % (id, p[property]))
                    if property=='hours':
                        try:
                            p[property]=str(p[property])
                            float(p[property])
                        except ValueError:
                            errors.append({'id': id , 'name': 'hours', 'value': p[property]})
                            add_warning(req,"チケット#%sの作業時間%sが実数ではありません。" % (id ,p[property]))
                    if property=='remainedhours':
                        try:
                            p[property]=str(p[property])
                            float(p[property])
                        except ValueError:
                            errors.append({'id': id , 'name': 'remainedhours', 'value': p[property]})
                            add_warning(req,"チケット#%sの残り作業時間%sが実数ではありません。" % (id ,p[property]))
                except ValueError:
                    pass
            self.log.debug(params)
            if len(errors)==0:
                self.update_tickets(req,cursor,db,params)
        user = req.authname
        def addMessage(s):
            messages.extend([s]);

        show_parent_ticket = self.config.getbool('steinschart','show_parent_ticket')
        if show_parent_ticket:
            tickets = self.get_nested_tickets(req, cursor)
#            for e in errors:
#                id = int(e['id'])
#                for ptickets in tickets.values():
#                    for ntickets in ptickets.values():
#                        for ticket in ntickets:
#                            if ticket['id']==id:
#                                ticket[e['name']]=e['value']
        
            
        else:
            tickets = self.get_tickets(req, cursor)
#            for e in errors:
#                id = int(e['id'])
#                for ticket in tickets:
#                    if ticket['id']==id:
#                        ticket[e['name']]=e['value']
        

        add_script(req, 'steinschart/js/worktime.js')
        add_stylesheet(req, 'common/css/report.css')
        if show_parent_ticket:
            return 'worktime_nestedticket.html', {'tickets':tickets, 'req':req, 'today': datetime.today()}, None
        else:
            return 'worktime.html', {'tickets':tickets, 'req':req, 'today': datetime.today()}, None
            
    def update_tickets(self, req, cursor, db, params):
        change_time = datetime.now(utc)
        for ticket_id in params:
            props = params[ticket_id]
            ticket = Ticket(self.env, ticket_id)

            changed = False

            if props.get("remainedhours"):
                current_totalhours = float(ticket["totalhours"])
                current_estimatedhours = float(ticket["estimatedhours"])
                
                hours = float(props.get("hours") or "0.0")
                remainedhours = float(props.get("remainedhours"))
                
                estimatedhours = current_totalhours + hours + remainedhours
                if hours > 0:
                    changed = True
                    ticket["totalhours"] = str(current_totalhours + hours)
                    cursor.execute(
                            "INSERT INTO ticket_change (ticket, time, author, field, oldvalue, newvalue) "
                            "VALUES (%s, %s, %s, 'hours', '0', %s)",
                            (ticket_id, to_utimestamp(change_time), req.authname, hours))
                if abs(current_estimatedhours - estimatedhours) >= 0.01:
                    changed = True
                    self.log.debug("change estimatedhours: %s -> %s" % 
                                    (current_estimatedhours, estimatedhours))
                    ticket["estimatedhours"] = str(estimatedhours)
                    
            close = (props.get("close") == "on")
            if close:
                changed = True
                ticket["status"] = "closed"

            for field, value in props.iteritems():
                if field in ("hours", "remainedhours", "close", "billable"):
                    continue
                changed = True
                ticket[field] = str(value)

            if not changed:
                continue

            ticket.save_changes(author=req.authname, when=change_time)
            db.commit()

    def get_nested_tickets(self, req, cursor):
        sql = """SELECT t.milestone ,sub.parent ,parent.summary ,t.id ,t.type ,t.owner, t.summary ,t.priority ,cth.value ,cbil.value , cest.value FROM ticket t
              JOIN ticket_custom cth ON t.id=cth.ticket
              JOIN ticket_custom cbil ON t.id=cbil.ticket
              JOIN ticket_custom cest ON t.id=cest.ticket
              LEFT JOIN subtickets sub ON t.id=sub.child
              LEFT JOIN ticket parent ON parent.id=sub.parent
              WHERE t.status <> 'closed' AND t.owner=%s AND
              cth.name='totalhours' AND cbil.name='billable' AND cest.name='estimatedhours'
              ORDER BY t.milestone, sub.parent"""
        cursor.execute(sql, (req.authname,))
        tickets = []
        ptickets = {}
        current_milestone=''
        current_psummary=''
        current_pid=0
        mtickets = {}
        for milestone, pid, psummary, id, type, owner, summary, priority, totalhours, billable, estimatedhours in cursor:
            # 初回のループの場合、初期化
            if current_pid == 0:
                current_milestone = milestone
                current_pid = pid
                current_psummary = psummary
                if not pid:
                    current_psummary = u'その他(親チケットなし)'
                
            billable = 'true' if billable=='1' else 'false'
            totalhours = totalhours if totalhours!='' else '0'
            estimatedhours = estimatedhours if totalhours!='' else '0'
            t = {'pid':pid, 'psummary':psummary, 'id':id ,'type':type, 'owner': owner, 'summary':summary, 'priority': priority , 'milestone':milestone, 'totalhours': totalhours, 'billable': billable, 'estimatedhours': estimatedhours}
            if milestone == current_milestone:
                if pid!= current_pid:
                    ptickets[(current_pid, current_psummary)]=tickets;
                    tickets = []
                tickets.append(t)
                current_pid = pid
                current_psummary = psummary
                if not pid:
                    current_psummary = u'その他(親チケットなし)'
            else:
                ptickets[(current_pid, current_psummary)]=tickets;
                mtickets[current_milestone] = ptickets
                ptickets = {}
                tickets = []
                current_milestone = milestone
                current_pid = pid
                current_psummary = psummary
                if not pid:
                    current_psummary = u'その他(親チケットなし)'
                tickets.append(t)
        if current_pid != 0:
            ptickets[(current_pid, current_psummary)]=tickets;                
            mtickets[current_milestone] = ptickets

        t = datetime.today()
        t = datetime(t.year,t.month,t.day)
        start = int(time.mktime(t.timetuple())*1e6)
        end = int(time.mktime(t.timetuple())*1e6+24*60*60*1e6)
        for ptickets in mtickets.values():
            for tickets in ptickets.values():
                for ticket in tickets:
                    sql = "SELECT * from ticket_change WHERE ticket=%s AND field='totalhours' AND time > %s  AND time < %s"
                    self.log.debug(sql)
                    cursor.execute(sql,  (ticket['id'], start, end))
                    row = cursor.fetchone()
                    if row:
                        ticket['reported'] = True
                    else:
                        ticket['reported'] = False
        return  mtickets

    def get_tickets(self, req, cursor):
        sql = """SELECT t.id ,t.type ,t.owner, t.summary ,t.priority ,t.milestone ,cth.value ,cbil.value , cest.value FROM ticket t
              JOIN ticket_custom cth ON t.id=cth.ticket
              JOIN ticket_custom cbil ON t.id=cbil.ticket
              JOIN ticket_custom cest ON t.id=cest.ticket
              WHERE t.status <> 'closed' AND t.owner=%s AND
              cth.name='totalhours' AND cbil.name='billable' AND cest.name='estimatedhours'
              ORDER BY t.milestone"""
        cursor.execute(sql, (req.authname,))
        tickets = []
        for id, type, owner, summary, priority, milestone, totalhours, billable, estimatedhours in cursor:
            billable = 'true' if billable=='1' else 'false'
            totalhours = totalhours if totalhours!='' else '0'
            estimatedhours = estimatedhours if totalhours!='' else '0'
            tickets.append({'id':id ,'type':type, 'owner': owner, 'summary':summary, 'priority': priority , 'milestone':milestone, 'totalhours': totalhours, 'billable': billable, 'estimatedhours': estimatedhours})

        t = datetime.today()
        t = datetime(t.year,t.month,t.day)
        start = int(time.mktime(t.timetuple())*1e6)
        end = int(time.mktime(t.timetuple())*1e6+24*60*60*1e6)

        for ticket in tickets:
            sql = "SELECT * from ticket_change WHERE ticket=%s AND field='totalhours' AND time > %s  AND time < %s" 
            self.log.debug(sql)
            cursor.execute(sql ,(ticket['id'], start, end))
            row = cursor.fetchone()
            if row:
                ticket['reported'] = True
            else:
                ticket['reported'] = False
        return  tickets

    # IRequestHandler methods
    def match_request(self, req):
        val = re.search('/worktime$', req.path_info)
        return val and val.start() == 0

    # ITemplateProvider
    def get_htdocs_dirs(self):
        """Return the absolute path of a directory containing additional
        static resources (such as images, style sheets, etc).
        """
        return [('worktime', resource_filename('tracsteinschart', 'htdocs'))]

    def get_templates_dirs(self):
        """Return the absolute path of the directory containing the provided
        genshi templates.
        """
        rtn = [resource_filename(__name__, 'templates')]
        return rtn
