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

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

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

import os
import types

from qt import *

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

from KdeQt import KQFileDialog, KQMessageBox

from LogForm import LogForm

class HgDiffDialog(LogForm, ui):
    """
    Class implementing a dialog to show the output of the hg diff command.
    """
    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.contentsGroup.setTitle(self.trUtf8('Difference:'))
        self.setCaption(self.trUtf8('Mercurial Diff'))
        QWhatsThis.add(self.contents,self.tr("<b>Mercurial Diff</b>\n"
            "<p>This shows the output of the hg diff command.</p>"
        ))
        QWhatsThis.add(self.errors,self.tr("<b>Mercurial diff errors</b>\n"
            "<p>This shows possible error messages of the hg diff"
            " command.</p>"
        ))
        
        self.setWFlags(self.getWFlags() | Qt.WDestructiveClose)
        self.vcs = vcs
        
        self.contents.setTextFormat(QTextEdit.LogText)
        
        self.cAdded = QColor(190, 237, 190)
        self.cRemoved = QColor(237, 190, 190)
        self.cLineNo = QColor(190, 190, 237)
        self.cNormal = self.contents.paletteBackgroundColor()
        
    def start(self, fn, versions=None):
        """
        Public slot to perform the diff command.
        
        @param fn filename to be diffed (string)
        @param versions list of versions to be diffed (list of up to 2 QString or None)
        """
        self.filename = fn
        
        self.contents.clear()
        self.contents.setBold(0)
        self.paras = 0
        
        cmdDict = {}
        if versions is not None:
            try:
                self.setActiveWindow()
                self.raiseW()
            except:
                pass
            if len(versions) == 1:
                cmdDict["rev"] = [str(versions[0])]
            else:
                cmdDict["rev"] = [str(versions[0]), str(versions[1])]
            
        if type(fn) is types.ListType:
            dname, fnames = self.vcs.splitPathList(fn)
        else:
            dname, fname = self.vcs.splitPath(fn)
            fn = [fn]
        
        # find the root of the repo
        repodir = str(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:
            pats = []
            for f in fn:
                f = str(f).replace(repodir, '') # make relative to repodir
                if f.startswith(os.sep):
                    f = f[1:]
                if f:
                    pats.append(f)
            repo = repository(self, repodir)
            cmdoptions = self.vcs._makeOptions("diff", cmdDict)
            revs = []
            if cmdoptions['rev']:
                revs = map(lambda x: repo.lookup(x), cmdoptions['rev'])
        
            files = []
            match = util.always
            if pats:
                roots, match, results = commands.makewalk(repo, pats, cmdoptions)
                for src, abs, rel, exact in results:
                    files.append(abs)
            commands.dodiff(self, self, repo, files, *revs, **{'match': match})
        except:
            pass
        os.chdir(cwd)
        
        # at exit
        self.closeButton.setFocus()
        
        if self.paras == 0:
            self.contents.append(\
                self.trUtf8('There is no difference.'))
            return
            
        self.saveButton.setEnabled(1)
        self.contents.setCursorPosition(0, 0)
        self.contents.ensureCursorVisible()
        
    def handleSave(self):
        """
        Private slot to handle the Save button press.
        
        It saves the diff shown in the dialog to a file in the local
        filesystem.
        """
        if type(self.filename) is types.ListType:
            if len(self.filename) > 1:
                fname = self.vcs.splitPathList(self.filename)[0]
            else:
                dname, fname = self.vcs.splitPath(self.filename[0])
                if fname != '.':
                    fname = "%s.diff" % self.filename[0]
                else:
                    fname = dname
        else:
            fname = self.vcs.splitPath(self.filename)[0]
            
        selectedFilter = QString('')
        fname = KQFileDialog.getSaveFileName(\
            fname,
            self.trUtf8("Patch Files (*.diff)"),
            self, None,
            self.trUtf8("Save Diff"),
            selectedFilter, 0)
        
        if fname.isEmpty():
            return
            
        ext = QFileInfo(fname).extension()
        if ext.isEmpty():
            ex = selectedFilter.section('(*',1,1).section(')',0,0)
            if not ex.isEmpty():
                fname.append(ex)
        if QFileInfo(fname).exists():
            abort = KQMessageBox.warning(self,
                self.trUtf8("Save Diff"),
                self.trUtf8("<p>The patch file <b>%1</b> already exists.</p>")
                    .arg(fname),
                self.trUtf8("&Overwrite"),
                self.trUtf8("&Abort"), QString.null, 1)
            if abort:
                return
        fname = unicode(QDir.convertSeparators(fname))
        
        try:
            f = open(fname, "wb")
            paras = self.contents.paragraphs()
            for i in range(paras):
                txt = self.contents.text(i)
                try:
                    f.write("%s%s" % (unicode(txt), os.linesep))
                except UnicodeError:
                    pass
            f.close()
        except IOError, why:
            KQMessageBox.critical(self, self.trUtf8('Save Diff'),
                self.trUtf8('<p>The patch file <b>%1</b> could not be saved.<br>Reason: %2</p>')
                    .arg(fn).arg(unicode(why)))

    ############################################################################
    # 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
        """
        self.contents.setTextFormat(QTextBrowser.PlainText)

        for a in args:
            for s in a.splitlines():
                self.contents.append(s)
                if s.startswith('+') or s.startswith('>'):
                    self.contents.setParagraphBackgroundColor(self.paras, self.cAdded)
                elif s.startswith('-') or s.startswith('<'):
                    self.contents.setParagraphBackgroundColor(self.paras, self.cRemoved)
                elif s.startswith('@@'):
                    self.contents.setParagraphBackgroundColor(self.paras, self.cLineNo)
                else:
                    self.contents.setParagraphBackgroundColor(self.paras, self.cNormal)
                self.paras += 1

    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'
