# -*- coding: utf-8 -*-

# Copyright (c) 2005 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing a dialog to show the output of the hg log command.
"""

import os

from qt import *

import mercurial.commands as commands
from mercurial.hg import repository
from mercurial.ui import ui

from LogForm import LogForm
from DiffDialog import HgDiffDialog

import Utilities

class HgLogDialog(LogForm, ui):
    """
    Module implementing a dialog to show the output of the hg log command.
    
    The dialog is nonmodal. Clicking a link in the upper text pane shows 
    a diff of the versions.
    """
    def __init__(self, vcs, parent = None):
        """
        Constructor
        
        @param vcs reference to the vcs object
        @param parent parent widget (QWidget)
        """
        LogForm.__init__(self, parent)
        ui.__init__(self, verbose = 0, debug = 0, quiet = 0, interactive = 1)
        
        self.saveButton.hide()
        
        QWhatsThis.add(self.contents,self.tr("<b>Mercurial Log</b>\n"
            "<p>This shows the output of the hg log command."
            " By clicking on the links you may show the difference"
            " between revisions.</p>"
        ))
        QWhatsThis.add(self.errors,self.tr("<b>Mercurial log errors</b>\n"
            "<p>This shows possible error messages of the hg log"
            " command.</p>"
        ))
        
        self.setWFlags(self.getWFlags() | Qt.WDestructiveClose)
        self.vcs = vcs
        
        self.disconnect(self.contents, SIGNAL('linkClicked(const QString&)'),
            self.contents, SLOT('setSource(const QString&)'))
        self.connect(self.contents, SIGNAL('linkClicked(const QString&)'),
            self.handleLinkClicked)
        self.connect(self.contents, SIGNAL('anchorClicked(const QString&, const QString&)'),
            self.handleAnchorClicked)
            
##        self.rx_sep = QRegExp('\\-+')
##        self.rx_sep2 = QRegExp('=+')
##        self.rx_rev = QRegExp('rev ([0-9]+):  ([^|]*) \| ([^|]*) \| ([0-9]+) lines?')
##        # "rev" followed by one or more decimals followed by a colon followed
##        # anything up to " | " (twice) followed by one or more decimals followed
##        # by "line" and an optional "s"
##        self.rx_rev2 = QRegExp('r([0-9]+) \| ([^|]*) \| ([^|]*) \| ([0-9]+) lines?')
##        # "r" followed by one or more decimals followed by " | " followed
##        # anything up to " | " (twice) followed by one or more decimals followed
##        # by "line" and an optional "s"
##        self.rx_flags = QRegExp('   ([ADM])( .*)')
##        # three blanks followed by A or D or M
##        self.rx_changed = QRegExp('Changed .*')
##        
##        self.flags = {
##            'A' : self.trUtf8('Added'),
##            'D' : self.trUtf8('Deleted'),
##            'M' : self.trUtf8('Modified')
##        }
##        
        self.revisions = []
        self.buf = []    # buffer for stdout
        
    def start(self, fn):
        """
        Public slot to start the cvs log command.
        
        @param fn filename to show the log for (string)
        """
        self.filename = fn
        self.dname, self.fname = self.vcs.splitPath(fn)

        try:
            self.setActiveWindow()
            self.raiseW()
        except:
            pass
        self.setCaption(self.trUtf8('Mercurial Log %1').arg(self.filename))
        
        # find the root of the repo
        repodir = str(self.dname)
        while not os.path.isdir(os.path.join(repodir, '.hg')):
            repodir = os.path.dirname(repodir)
            if repodir == os.sep:
                return
        
        cwd = os.getcwd()
        os.chdir(repodir)
        try:
            fn = str(fn).replace(repodir, '') # make relative to repodir
            if fn.startswith(os.sep):
                fn = fn[1:]
            if os.path.isdir(fn):
                self.contents.clear()
                self.errors.append(self.trUtf8(\
                    "'%1' is a directory. Please select a file to show the log for."
                ).arg(fn))
            else:
                repo = repository(self, repodir)
                cmdoptions = self.vcs._makeOptions("log", {"patch" : 0})
                commands.log(self, repo, fn, **cmdoptions)
        except:
            pass
        self.processBuffer()
        os.chdir(cwd)
        
        # at exit
        self.closeButton.setFocus()
        
        self.contents.setCursorPosition(0, 0)
        self.contents.ensureCursorVisible()
        
    def processBuffer(self):
        """
        Private method to process and display the buffer.
        
        After the hg log command has finished, diff links are inserted into
        the contents pane.
        """
        self.closeButton.setFocus()
        
        self.contents.clear()
        if not self.buf:
            self.errors.append(self.trUtf8("No log available for '%1'").arg(self.filename))
            return
        
        lvers = 1
        insertsep = 0
        for s in self.buf:
            if s.startswith("changeset:"):
                if insertsep:
                    self.contents.append('<hr>')
                else:
                    insertsep = 1
                dstr = QString('<b>%1</b>').arg(s)
                ver = s.split()[1].split(':')[0]
                try:
                    lv = self.revisions[lvers]
                    lvers += 1
                except IndexError:
                    lv = None
                
                if lv is not None:
                    url = QUrl()
                    url.setProtocol("file")
                    url.setFileName(self.filename)
                    query = QString()
                    query.append(lv).append('_').append(ver)
                    url.setQuery(query)
                    dstr.append(' [<a href="')\
                        .append(url.toString())\
                        .append('" name="')\
                        .append(query)\
                        .append('">')\
                        .append(self.trUtf8('diff to %1').arg(lv))\
                        .append('</a>]')
                self.contents.append(dstr)
                
            elif s.startswith("user:"):
                dstr = QString('<i>%1</i>').arg(s)
                self.contents.append(dstr)
                
            elif s.startswith("date:"):
                dstr = QString('<i>%1</i>').arg(s)
                self.contents.append(dstr)
                
            else:
                if s:
                    self.contents.append(Utilities.html_encode(unicode(s)))
                else:
                    s = self.contents.append('<br>')
        
        self.contents.setCursorPosition(0, 0)
        self.contents.ensureCursorVisible()
        
    def handleLinkClicked(self, link):
        """
        Private slot to handle the linkClicked signal of the contents pane.
        
        @param link the link that was clicked (QString)
        """
        self.contents.setSource('')
        link = QUrl(link)
        filename = link.path()
        ver = link.query()
        v1 = ver.section('_', 0, 0)
        v2 = ver.section('_', 1, 1)
        if v1.isEmpty() or v2.isEmpty():
            return
            
        self.diff = HgDiffDialog(self.vcs)
        self.diff.show()
        self.diff.start(filename, [v1, v2])
        
    def handleAnchorClicked(self, name, link):
        """
        Private slot to handle the anchorClicked signal of the contents pane.
        
        @param name name of the anchor that was clicked (QString)
        @param link the link that was clicked (QString)
        """
        self.contents.scrollToAnchor(name)

    ############################################################################
    # Overridden methods from mercurial.ui
    ############################################################################
    
    def write(self, *args):
        """
        Public method to write something to the output of the dialog.
        
        @param *args texts to be written
        """
        for a in args:
            for s in a.splitlines():
                self.buf.append(s)
                if s.startswith("changeset:"):
                    ver = s.split()[1].split(':')[0]
                    # save revision number for later use
                    self.revisions.append(ver)

    def write_err(self, *args):
        """
        Public method to write something to the errors output of the dialog.
        
        @param *args error texts to be written
        """
        for a in args:
            self.errors.append(unicode(a))

    def prompt(self, msg, pat, default = "y"):
        """
        Public method to prompt the user for some input.
        
        @param msg prompt message to be shown (string)
        @param pat pattern of acceptable input (string)
        @param default default answer if we are in noninteractive mode (string)
        @return the entered text
        """
        raise RuntimeError, 'Not implemented'
    
    def status(self, *msg):
        """
        Public method to output a status message.
        
        @param status messages to show (strings)
        """
        if not self.quiet:
            self.write(*msg)
    
    def warn(self, *msg):
        """
        Public method to output a warning message.
        
        @param warning messages to show (strings)
        """
        self.write_err(*msg)
    
    def note(self, *msg):
        """
        Public method to output a note.
        
        @param notes to show (strings)
        """
        if self.verbose:
            self.write(*msg)
    
    def debug(self, *msg):
        """
        Public method to output a debug message.
        
        @param debug messages to show (strings)
        """
        if self.debugflag:
            self.write(*msg)
    
    def edit(self, text):
        """
        Public method to enter some text.
        
        @exception RuntimeError not implemented
        """
        raise RuntimeError, 'Not implemented'
