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

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

"""
Module implementing a dialog to show the output of the Mercurial status command.
"""

import types
import os

from qt import *

from KdeQt import KQMessageBox

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

from StatusForm import StatusForm
import Preferences

class HgStatusDialog(StatusForm, ui):
    """
    Module implementing a dialog to show the output of the Mercurial status command.
    """
    def __init__(self, vcs, parent = None):
        """
        Constructor
        
        @param vcs -- reference to the vcs object
        @param parent -- parent widget (QWidget)
        """
        StatusForm.__init__(self, parent)
        ui.__init__(self, verbose = 0, debug = 0, quiet = 0, interactive = 1)
        
        self.setWFlags(self.getWFlags() | Qt.WDestructiveClose)
        self.vcs = vcs
        
        self.statusList.setSorting(0)
        
        self.menu = QPopupMenu()
        self.addItem = self.menu.insertItem(self.trUtf8("Add to repository"),
            self.handleAdd)
        self.forgetItem = self.menu.insertItem(self.trUtf8("Forget"),
            self.handleForget)
        self.menu.insertSeparator()
        self.commitItem = self.menu.insertItem(self.trUtf8("Commit changes to repository..."),
            self.handleCommit)
        self.menu.setItemEnabled(self.addItem, 0)
        self.menu.setItemEnabled(self.forgetItem, 0)
        self.menu.setItemEnabled(self.commitItem, 0)
        
        self.connect(self.statusList,SIGNAL('contextMenuRequested(QListViewItem *, const QPoint &, int)'),
                     self.handleContextMenu)
            
        self.addedIndicator = self.trUtf8('added')
        self.unversionedIndicator = self.trUtf8('unversioned')
        self.deletedIndicator = self.trUtf8('deleted')
        self.modifiedIndicator = self.trUtf8('modified')
        
        self.modifiedIndicators = QStringList()
        self.modifiedIndicators.append(self.addedIndicator)
        self.modifiedIndicators.append(self.deletedIndicator)
        self.modifiedIndicators.append(self.modifiedIndicator)
        
    def handleContextMenu(self,itm,coord,col):
        """
        Private slot to show the context menu of the listview.
        
        @param itm the selected listview item (QListViewItem)
        @param coord the position of the mouse pointer (QPoint)
        @param col the column of the mouse pointer (int)
        """
        self.menu.popup(coord)
        
    def start(self, fn):
        """
        Public slot to start the hg status command.
        
        @param fn filename(s) to show the status of (string or list of strings)
        """
        self.args = fn
        
        try:
            self.setActiveWindow()
            self.raiseW()
        except:
            pass
        
        if type(fn) is types.ListType:
            self.dname, fnames = self.vcs.splitPathList(fn)
        else:
            self.dname, fname = self.vcs.splitPath(fn)
            fn = [fn]
        
        self.setCaption(self.trUtf8('Mercurial Status'))

        # 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:
            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("status", {})
            _cwd = repo.getcwd()
            files, matchfn = commands.matchpats(repo, _cwd, pats, cmdoptions)
            (c, a, d, u) = [[util.pathto(_cwd, x) for x in n]
                            for n in repo.changes(files=files, match=matchfn)]
        
            changetypes = [('modified', self.modifiedIndicator, c),
                           ('added', self.addedIndicator, a),
                           ('removed', self.deletedIndicator, d),
                           ('unknown', self.unversionedIndicator, u)]
        
            for opt, status, changes in changetypes:
                for f in changes:
                    itm = QListViewItem(self.statusList, status, f)
        except:
            pass
        os.chdir(cwd)
    
        self.menu.setItemEnabled(self.addItem, 1)
        self.menu.setItemEnabled(self.forgetItem, 1)
        self.menu.setItemEnabled(self.commitItem, 1)
        
    def handleCommit(self):
        """
        Private slot to handle the Commit context menu entry.
        """
        names = [os.path.join(self.dname, unicode(itm.text(1))) \
                 for itm in self.getModifiedItems()]
        if not names:
            KQMessageBox.information(self,
                self.trUtf8("Commit"),
                self.trUtf8("""There are no uncommitted changes available/selected."""),
                self.trUtf8("&OK"),
                QString.null,
                QString.null,
                0, -1)
            return
        
        if Preferences.getVCS("AutoSaveFiles"):
            vm = qApp.mainWidget().getViewManager()
            for name in names:
                vm.saveEditor(name)
        self.vcs.vcsCommit(names, '', parent=self)
        self.refreshStatus()
        
    def handleAdd(self):
        """
        Private slot to handle the Add context menu entry.
        """
        names = [os.path.join(self.dname, unicode(itm.text(1))) \
                 for itm in self.getUnversionedItems()]
        if not names:
            KQMessageBox.information(self,
                self.trUtf8("Commit"),
                self.trUtf8("""There are no unversioned files available/selected."""),
                self.trUtf8("&OK"),
                QString.null,
                QString.null,
                0, -1)
            return
        
        self.vcs.vcsAdd(names)
        self.refreshStatus()
        for browser in qApp.mainWidget().getProjectBrowser().getProjectBrowsers():
            browser.rebuildTree()
        
    def handleForget(self):
        """
        Private slot to handle the Forget context menu entry.
        """
        names = [os.path.join(self.dname, unicode(itm.text(1))) \
                 for itm in self.getAddedItems()]
        if not names:
            KQMessageBox.information(self,
                self.trUtf8("Commit"),
                self.trUtf8("""There are no added files available/selected."""),
                self.trUtf8("&OK"),
                QString.null,
                QString.null,
                0, -1)
            return
        
        self.vcs.hgForget(names)
        self.refreshStatus()
        for browser in qApp.mainWidget().getProjectBrowser().getProjectBrowsers():
            browser.rebuildTree()
        
    def getModifiedItems(self):
        """
        Private method to retrieve all entries, that have a modified status.
        
        @return list of all items with a modified status
        """
        modifiedItems = []
        itm = self.statusList.firstChild()
        while itm:
            if itm.isSelected() and \
                    self.modifiedIndicators.contains(itm.text(0)):
                modifiedItems.append(itm)
            itm = itm.itemBelow()
        return modifiedItems
        
    def getUnversionedItems(self):
        """
        Private method to retrieve all entries, that have an unversioned status.
        
        @return list of all items with a modified status
        """
        unversionedItems = []
        itm = self.statusList.firstChild()
        while itm:
            if itm.isSelected() and \
                    self.unversionedIndicator.compare(itm.text(0)) == 0:
                unversionedItems.append(itm)
            itm = itm.itemBelow()
        return unversionedItems
        
    def getAddedItems(self):
        """
        Private method to retrieve all entries, that have an adedd status.
        
        @return list of all items with a modified status
        """
        addedItems = []
        itm = self.statusList.firstChild()
        while itm:
            if itm.isSelected() and \
                    self.addedIndicator.compare(itm.text(0)) == 0:
                addedItems.append(itm)
            itm = itm.itemBelow()
        return addedItems

    def refreshStatus(self):
        """
        Private slot to refresh the status display.
        """
        self.okButton.setDefault(1)
        self.okButton.setFocus()
        
        self.refreshButton.setEnabled(0)
        
        self.menu.setItemEnabled(self.addItem, 0)
        self.menu.setItemEnabled(self.forgetItem, 0)
        self.menu.setItemEnabled(self.commitItem, 0)
        
        self.statusList.clear()
        
        self.start(self.args)

    ############################################################################
    # 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:
            self.errors.moveCursor(QTextEdit.MoveEnd, 0)
            self.errors.insert(unicode(a))

    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
        """
        if not self.interactive:
            return default
        
        while 1:
            self.write(msg, " ")
            while not self.inputText:
                qApp.processEvents()
            r = self.inputText
            self.inputText = ''
            if re.match(pat, r):
                return r
            else:
                self.write("unrecognized response\n")
    
    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'
