# -*- coding: iso-8859-1 -*-
#
# Copyright (C) 2005 Edgewall Software
# Copyright (C) 2005,2006 Lele Gaifax <lele@metapensiero.it>
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.com/license.html.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://projects.edgewall.com/trac/.
#
# Author: Lele Gaifax <lele@metapensiero.it>

from trac.versioncontrol.cache import CachedRepository, CachedChangeset

class DarcsCachedChangeset(CachedChangeset):
    """
    Darcs version of the CachedChangeset that knows about the hash.
    """

    def __init__(self, rev, db, authz=None):
        CachedChangeset.__init__(self, rev, db, authz)
        cursor = self.db.cursor()
        cursor.execute("SELECT hash FROM revision_hash WHERE rev=%s", (rev,))
        row = cursor.fetchone()
        self.hash = row[0]

class DarcsCachedRepository(CachedRepository):
    """
    Darcs version of the cached repository, that serves DarcsCachedChangesets
    """

    def get_changeset(self, rev):
        if not self.synced:
            self.sync()
            self.synced = 1
        return DarcsCachedChangeset(self.repos.normalize_rev(rev), self.db,
                                    self.authz)

    def sync(self):
        # As I wrote in http://projects.edgewall.com/trac/ticket/2733,
        # a better approach would be delegating to the changeset the
        # details of inserting itself in the database cache.  Since
        # nothing suggests this cross the near future vision of trac
        # guys, and I'm way too lazy to keep syncing the easy patch, I
        # did a step back and chose to reimplement the method in this
        # private Darcs subclass. OTOH, this effectively means that
        # now the plugin is completely usable *without* patching
        # upstream trac, if not for the 'hash' field in the revision
        # table!
        #
        # Hopefully, the behaviour either won't change so often, or they
        # provide a nice equivalent :-)

        from trac.versioncontrol.cache import _kindmap, _actionmap
        from trac.versioncontrol import Authorizer

        self.log.debug("Checking whether sync with repository is needed")
        cursor = self.db.cursor()

        # -- repository used for populating the cache
        cursor.execute("SELECT value FROM system WHERE name='repository_dir'")
        row = cursor.fetchone()
        if row:
            previous_repository_dir = row[0]
        else: # no 'repository_dir' stored yet, assume everything's OK
            previous_repository_dir = self.name

        if self.name != previous_repository_dir:
            raise TracError, ("The 'repository_dir' has changed, "
                              "a 'trac-admin resync' operation is needed.")

        # Maybe this is a resync. The only way to detect that is
        # by checking the revision table: if it's empty then we
        # are either at the first sync, or at a resync. In both
        # cases we are safe in dropping the revision_hash content.
        cursor.execute("SELECT rev FROM revision")
        row = cursor.fetchone()
        if not row:               
            cursor.execute("DELETE FROM revision_hash")

        youngest_stored = self.repos.get_youngest_rev_in_cache(self.db)

        if youngest_stored != str(self.repos.youngest_rev):
            authz = self.repos.authz
            self.repos.authz = Authorizer() # remove permission checking

            kindmap = dict(zip(_kindmap.values(), _kindmap.keys()))
            actionmap = dict(zip(_actionmap.values(), _actionmap.keys()))
            self.log.info("Syncing with repository (%s to %s)"
                          % (youngest_stored, self.repos.youngest_rev))
            if youngest_stored:
                current_rev = self.repos.next_rev(youngest_stored)
            else:
                try:
                    current_rev = self.repos.oldest_rev
                    current_rev = self.repos.normalize_rev(current_rev)
                except TracError:
                    current_rev = None
            while current_rev is not None:
                changeset = self.repos.get_changeset(current_rev)
                cursor.execute("INSERT INTO revision (rev,time,author,message) "
                               "VALUES (%s,%s,%s,%s)", (str(current_rev),
                               changeset.date, changeset.author,
                               changeset.message))
                cursor.execute("INSERT INTO revision_hash (rev,hash) "
                               "VALUES (%s,%s)", (str(current_rev),
                                                  changeset.hash))
                for path,kind,action,base_path,base_rev in changeset.get_changes():
                    self.log.debug("Caching node change in [%s]: %s"
                                   % (current_rev, (path, kind, action,
                                      base_path, base_rev)))
                    kind = kindmap[kind]
                    action = actionmap[action]
                    cursor.execute("INSERT INTO node_change (rev,path,"
                                   "node_type,change_type,base_path,base_rev) "
                                   "VALUES (%s,%s,%s,%s,%s,%s)",
                                   (str(current_rev), path, kind, action,
                                   base_path, base_rev))
                current_rev = self.repos.next_rev(current_rev)
            self.db.commit()
            self.repos.authz = authz # restore permission checking
