# -*- coding: utf-8 -*-
"""
Created on 2010/08/07
@author: @tomosnowbug
"""
from genshi.builder import tag
from trac.core import Component, implements
from trac.ticket.model import Ticket
from trac.versioncontrol.api import IRepositoryChangeListener
from trac.web.api import IRequestFilter
from trac.wiki.api import IWikiSyntaxProvider
import re




class MultiRepositoryHelperPlugin(Component):
    """
    Trac plugin for Multiple Repository environment.
    when one repository assign to multiple trac project
    (ex proj_main and proj_other),
    describe like refs #proj_main/1 in your commit log,
    only add ticket comment at proj_main project's ticket No.1.
    also #proj_main/1 link to proj_main's ticket No.1 globally
    without settings.
    """
    
    def __init__(self):
        self._absolute_url = None
    
    implements(IRepositoryChangeListener, IWikiSyntaxProvider, IRequestFilter)

    def changeset_added(self, repos, changeset):
        """
        IRepositoryChangeListener#changeset_added
        repos is SvnCachedRepository(Repository) object 
        changeset is CachedChangeset(Changeset) object
        """
        
        self.log.debug(" *** call changeset_added.")
        self._update_ticket(repos, changeset)

    def changeset_modified(self, repos, changeset, old_changeset):
        """
        IRepositoryChangeListener#changeset_modified
        """
        self.log.debug(" *** call changeset_modified.")
        self._update_ticket(repos, changeset)
        
    def _update_ticket(self, repos, changeset):
        author = changeset.author
        ticket_comment = changeset.message
        reponame = repos.reponame
        revision = changeset.rev
        self.update_ticket(reponame, revision, author, ticket_comment)
        
    def update_ticket(self, reponame, revision, author, ticket_comment):
        myproj = self.env.project_name
        self.log.debug("myproj=" + myproj)
        list = TicketUtil.extract_ticket_ids(ticket_comment)
        self.log.debug(list.__str__())
        for (proj, ticket_id) in list:
            ticket_expression = TicketUtil.get_ticket_expresson(proj, ticket_id)
            self.log.debug(ticket_expression + ",proj=" +\
                           proj + ", id=" + ticket_id)
            if proj == myproj:
                self.log.info("update ticket [" + proj + ":#" + ticket_id + "]")
                additional_comment = "in [" + str(revision) + "/" \
                                            + reponame + "] \n"
                
                # update ticket
                ticket = Ticket(self.env, ticket_id)
                if ticket.exists :
                    ticket.save_changes(author, \
                                         additional_comment + ticket_comment)
                else:
                    self.log.warn("ticket " + ticket_expression \
                                  + " is not exists")
    
    def get_wiki_syntax(self):
        """
        IWikiSyntaxProvider#get_wiki_syntax
        """
        self.log.debug(" **** call get_wiki_syntax")
        return [(TicketUtil._regex_ticket_refs,
                  self._syntax_formatter)]

    def get_link_resolvers(self):
        """
        IWikiSyntaxProvider#get_link_resolvers
        """
        return []
    
    def _syntax_formatter(self, formatter, ns, match):
        self.log.debug(" **** call _syntax_formatter")
        list = TicketUtil.extract_ticket_ids(ns)
        return TicketUtil.create_link(self._absolute_url, list)
    
    def pre_process_request(self, req, handler):
        """
        IRequestFilter#pre_process_request
        """
        if self._absolute_url != None:
            return handler
        
        self._absolute_url = re.sub(r"/[^/]+$", "", req.base_url) #remove project's path
        self.log.debug("absolute url is [" + self._absolute_url + "]")
        return handler

    def post_process_request(self, req, template, data, content_type):
        """
        IRequestFilter#post_process_request
        """
        return (template, data, content_type)

class TicketUtil:
    """
    utility class for extract ticket id from commit comment
    """
    _prefix_expression = "refs "
    _ticket_id_symbol = "#"
    _delimiter_symbol = "/"
    
    #regexp (refs project_name:#ticket_number)
    _regex_ticket_refs = r"%s%s([^%s]+)%s(\d+)" % \
                                (_prefix_expression,
                                 _ticket_id_symbol, 
                                 _delimiter_symbol, 
                                 _delimiter_symbol)
    _ticket_expression_ = _ticket_id_symbol + "%s" \
                                + _delimiter_symbol + "%s"
    
    @staticmethod
    def get_ticket_expresson(project_name, ticket_id):
        return TicketUtil._ticket_expression_ % (project_name, ticket_id)
    
    @staticmethod
    def extract_ticket_ids(extract_str):
        """
        returns [(project_name, ticket_id), (project_name, ticket_id)...]
        """
        result = re.compile(TicketUtil._regex_ticket_refs).findall(extract_str)
        return result
    
    @staticmethod
    def create_link(absolute_url, tuple_proj_ticket_id):
        """
        returns genshi.tag object
        """
        ret = tag.span("refs ")
        for (proj, ticket_number) in tuple_proj_ticket_id:
            url = absolute_url + "/" + proj + "/ticket/" + ticket_number
            link_contents = TicketUtil._ticket_expression_ % \
                                 (proj, ticket_number)
            ret += tag.a(link_contents, href=url)
            
        return ret

