'''
Created on 2010/09/12

@author: tacky21jp
'''
import re
from datetime import datetime

from trac.core import *
from trac.web import IRequestHandler
from trac.env import IEnvironmentSetupParticipant
from trac.web.chrome import INavigationContributor, ITemplateProvider, add_stylesheet, add_script, add_link
from trac.perm import IPermissionRequestor
from trac.mimeview.api import Context
from trac.resource import ResourceNotFound
from trac.util import sorted, embedded_numbers
from trac.config import Configuration

from trac.versioncontrol.api import NoSuchChangeset, NoSuchNode
from trac.versioncontrol.web_ui.util import *

from genshi.builder import tag
from stepcounter.StepCounterBuilder import *

class StepCounterPlugin(Component):
    implements(IEnvironmentSetupParticipant,
                    INavigationContributor, IRequestHandler, ITemplateProvider,IPermissionRequestor)

    counterBuilder = StepCounterBuilder()

    # INavigationContributor methods
    def get_active_navigation_item(self, req):
        return 'StepCounter'

    def get_navigation_items(self, req):
        default_path = Configuration.get(self.config, 'stepcounter', 'default_path', default='/')

        yield ('mainnav', 'StepCounter',
               tag.a('StepCounter', href=req.href.stepcounter(default_path)))

    # IEnvironmentSetupParticipant methods
    def environment_created(self):
        pass

    def environment_needs_upgrade(self, db):
        return False

    def upgrade_environment(self, db):
        pass

    # IRequestHandler methods
    def match_request(self, req):
        match = re.match(r'/(stepcounter)(/.*)?$', req.path_info)
        if match:
            mode, path = match.groups()
            req.args['path'] = path or '/'
            return True


    def process_request(self, req):
        req.perm.assert_permission('BROWSER_VIEW')

        path = req.args.get('path', '/')
        rev = req.args.get('rev', None)
        order = req.args.get('order', None)
        desc = req.args.get('desc', None)
        xhr = req.get_header('X-Requested-With') == 'XMLHttpRequest'
        commiter = req.args.get('commiter', None)

        repos = self.env.get_repository(req.authname)

        try:
            if rev:
                rev = repos.normalize_rev(rev)
            rev_or_latest = rev or repos.youngest_rev
            node = get_existing_node(req, repos, path, rev_or_latest)
        except NoSuchChangeset, e:
            raise ResourceNotFound(e.message, _('Invalid Changeset Number'))

        context = Context.from_request(req, 'source', path, rev_or_latest)

        path_links = self.get_counter_path_links(req.href, path, rev, order, desc)
        if len(path_links) > 1:
            try:
                add_link(req, 'up', path_links[-2]['href'], _('Parent directory'))
            except Exception,e:
                pass

        dir = node.isdir and self.count_dir(req, repos, node, rev, commiter)

        if dir:
            sum_step=0
            sum_comment=0
            for r in dir['counts']:
                if r.step:
                    sum_step = sum_step + r.step
                if r.commentStep:
                    sum_comment = sum_comment + r.commentStep
            dir['sum_step']=sum_step
            dir['sum_comment']=sum_comment

        data = {
            'context': context,
            'path': path, 'rev': node.rev, 'stickyrev': rev,
            'created_path': node.created_path,
            'created_rev': node.created_rev,
            'path_links': path_links,
            'dir':dir
        }

        add_stylesheet(req, 'stepcounter/css/stepcounter.css')

        return 'stepcounter.html',data, None

    # ITemplateProvider methods
    def get_templates_dirs(self):
        from pkg_resources import resource_filename
        return [resource_filename(__name__, 'templates')]

    def get_htdocs_dirs(self):
        from pkg_resources import resource_filename
        return [('stepcounter',resource_filename(__name__, 'htdocs'))]

    #IPermissionRequestor methods
    def get_permission_actions(self):
        return ['BROWSER_VIEW ','BROWSER_VIEW ']


    # Internal methods

    def count_dir(self, req, repos, node, rev=None, commiter=None):

        # Entries metadata
        class entry(object):
            __slots__ = 'name rev kind isdir path content_length'.split()
            def __init__(self, node):
                for f in self.__slots__:
                    setattr(self, f, getattr(node, f))

        entries = [entry(n) for n in node.get_entries()]
        result = []
        count_result = []

        changes = get_changes(repos, [i.rev for i in entries])

        for e in entries:
            if not(e.isdir):
                _node = get_existing_node(req, repos, e.path, rev)
                rev_or_latest = rev or repos.youngest_rev
                if commiter is None or changes[e.rev] is None or (changes[e.rev] and commiter == changes[e.rev].author):
                    context = Context.from_request(req, 'source', e.path, rev_or_latest)
                    count = self.count_file(req,context,repos,_node,rev)

                    if count:
                        result.append(e)
                        count_result.append(count)
        for e in entries:
            if e.isdir:
                _node = get_existing_node(req, repos, e.path, rev)
                rev_or_latest = rev or repos.youngest_rev
                _dat = self.count_dir(req, repos, _node, rev_or_latest, commiter)

                result.extend(_dat['entries'])
                count_result.extend(_dat['counts'])

        if rev:
            newest = repos.get_changeset(rev).date
        else:
            newest = datetime.now(req.tz)

         # Ordering of entries
        order = req.args.get('order', 'name').lower()
        desc = req.args.has_key('desc')

        if order == 'date':
            def file_order(a):
                return changes[a.rev].date
        elif order == 'size':
            def file_order(a):
                return (a.content_length,
                        embedded_numbers(a.name.lower()))
        else:
            def file_order(a):
                return embedded_numbers(a.name.lower())

        dir_order = desc and 1 or -1

        def browse_order(a):
            return a.isdir and dir_order or 0, file_order(a)
        entries = sorted(entries, key=browse_order, reverse=desc)


        add_script(req, 'common/js/expand_dir.js')
        add_script(req, 'common/js/keyboard_nav.js')

        return {'order': order, 'desc': desc and 1 or None,
                'entries': result, 'changes': changes, 'counts':count_result,
                }

    def count_file(self, req, context, repos, node, rev=None):

        result = None

        counter = self.counterBuilder.getCounter(node.path)

        if counter:
            content = node.get_content()
            result = counter.countContent(content.read())
            #self.log.debug('%s' % node.path)
        else:
            #result = StepCountResult('','',0,0,0)
            result = None
        return result

    def get_counter_path_links(self,href, fullpath, rev, order=None, desc=None):
        links = [{'name': 'root',
                  'href': href.stepcounter(rev=rev, order=order, desc=desc)}]
        path = ''
        for part in [p for p in fullpath.split('/') if p]:
            path += part + '/'
            links.append({
                'name': part,
                'href': href.stepcounter(path, rev=rev, order=order, desc=desc)
                })
        return links