# thgstrip.py - MQ strip dialog for TortoiseHg
#
# Copyright 2009 Yuki KODAMA <endflow.net@gmail.com>
# Copyright 2010 David Wilhelm <dave@jumbledpile.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.

from PyQt4.QtCore import *
from PyQt4.QtGui import *

from mercurial import error

from tortoisehg.util import hglib
from tortoisehg.hgqt.i18n import _, ngettext
from tortoisehg.hgqt import cmdcore, cmdui, cslist, qtlib

class StripWidget(cmdui.AbstractCmdWidget):
    """Command widget to strip changesets"""

    def __init__(self, repoagent, rev=None, parent=None, opts={}):
        super(StripWidget, self).__init__(parent)
        self._repoagent = repoagent

        grid = QGridLayout()
        grid.setContentsMargins(0, 0, 0, 0)
        grid.setSpacing(6)
        self.setLayout(grid)

        ### target revision combo
        self.rev_combo = combo = QComboBox()
        combo.setEditable(True)
        grid.addWidget(QLabel(_('Strip:')), 0, 0)
        grid.addWidget(combo, 0, 1)
        grid.addWidget(QLabel(_('Preview:')), 1, 0, Qt.AlignLeft | Qt.AlignTop)
        self.status = QLabel("")
        grid.addWidget(self.status, 1, 1, Qt.AlignLeft | Qt.AlignTop)

        if rev is None:
            rev = self.repo.dirstate.branch()
        else:
            rev = str(rev)
        combo.addItem(hglib.tounicode(rev))
        combo.setCurrentIndex(0)
        for name in self.repo.namedbranches:
            combo.addItem(name)

        tags = list(self.repo.tags())
        tags.sort(reverse=True)
        for tag in tags:
            combo.addItem(hglib.tounicode(tag))

        ### preview box, contained in scroll area, contains preview grid
        self.cslist = cslist.ChangesetList(self.repo)
        cslistrow = 2
        cslistcol = 1
        grid.addWidget(self.cslist, cslistrow, cslistcol)

        ### options
        optbox = QVBoxLayout()
        optbox.setSpacing(6)
        grid.addWidget(QLabel(_('Options:')), 3, 0, Qt.AlignLeft | Qt.AlignTop)
        grid.addLayout(optbox, 3, 1)

        self.discard_chk = QCheckBox(_('Discard local changes, no backup '
                                       '(-f/--force)'))
        self.nobackup_chk = QCheckBox(_('No backup (-n/--nobackup)'))
        optbox.addWidget(self.discard_chk)
        optbox.addWidget(self.nobackup_chk)

        self.discard_chk.setChecked(bool(opts.get('force')))
        self.nobackup_chk.setChecked(bool(opts.get('nobackup')))

        grid.setRowStretch(cslistrow, 1)
        grid.setColumnStretch(cslistcol, 1)

        # signal handlers
        self.rev_combo.editTextChanged.connect(self.preview)
        self.discard_chk.toggled.connect(self.preview)

        # prepare to show
        self.rev_combo.lineEdit().selectAll()
        self.cslist.setHidden(False)
        self.preview()

    ### Private Methods ###

    @property
    def repo(self):
        return self._repoagent.rawRepo()

    def get_rev(self):
        """Return the integer revision number of the input or None"""
        revstr = hglib.fromunicode(self.rev_combo.currentText())
        if not revstr:
            return None
        try:
            rev = self.repo[revstr].rev()
        except (error.RepoError, error.LookupError):
            return None
        return rev

    def updatecslist(self, uselimit=True):
        """Update the cs list and return the success status as a bool"""
        rev = self.get_rev()
        if rev is None:
            return False
        striprevs = list(self.repo.changelog.descendants([rev]))
        striprevs.append(rev)
        striprevs.sort()
        self.cslist.clear()
        self.cslist.update(striprevs)
        return True

    @pyqtSlot()
    def preview(self):
        if self.updatecslist():
            striprevs = self.cslist.curitems
            cstext = ngettext(
                "<b>%d changeset</b> will be stripped",
                "<b>%d changesets</b> will be stripped",
                len(striprevs)) % len(striprevs)
            self.status.setText(cstext)
        else:
            self.cslist.clear()
            self.cslist.updatestatus()
            cstext = qtlib.markup(_('Unknown revision!'), fg='red',
                                  weight='bold')
            self.status.setText(cstext)
        self.commandChanged.emit()

    def canRunCommand(self):
        return self.get_rev() is not None

    def runCommand(self):
        # Note that we have discussed that --hidden should only be passed to
        # mercurial commands when hidden revisions are shown.
        # However in the case of strip we can always pass it --hidden safely,
        # since strip will always strip all the descendants of a revision.
        # Thus in this case --hidden will just let us choose a hidden revision
        # as the base revision to strip.
        opts = {'verbose': True,
                'hidden': True,
                'force': self.discard_chk.isChecked(),
                'nobackup': self.nobackup_chk.isChecked(),
                }

        wc = self.repo[None]
        if not opts['force'] and (wc.modified() or wc.added() or wc.removed()):
            main = _("Detected uncommitted local changes.")
            text = _("Do you want to discard them and continue?")
            labels = ((QMessageBox.Yes, _('&Yes (--force)')),
                      (QMessageBox.No, _('&No')))
            if qtlib.QuestionMsgBox(_('Confirm Strip'), main, text,
                                    labels=labels, parent=self):
                opts['force'] = True
            else:
                return cmdcore.nullCmdSession()

        rev = self.rev_combo.currentText()
        cmdline = hglib.buildcmdargs('strip', rev, **opts)
        return self._repoagent.runCommand(cmdline, self)


def createStripDialog(repoagent, rev=None, parent=None, opts={}):
    dlg = cmdui.CmdControlDialog(parent)
    repo = repoagent.rawRepo()
    dlg.setWindowIcon(qtlib.geticon('menudelete'))
    dlg.setWindowTitle(_('Strip - %s') % repo.displayname)
    dlg.setObjectName('strip')
    dlg.setRunButtonText(_('&Strip'))
    dlg.setCommandWidget(StripWidget(repoagent, rev, dlg, opts))
    return dlg
