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

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

"""
Module implementing a dialog to edit the history modification plan.
"""


from PyQt5.QtCore import pyqtSlot, QCoreApplication
from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QComboBox

from E5Gui import E5MessageBox

from Ui_HgHisteditPlanEditor import Ui_HgHisteditPlanEditor

import UI.PixmapCache


class HgHisteditPlanActionComboBox(QComboBox):
    """
    Class implementing a combo box to select the action in the plan tree.
    """
    def __init__(self, item, column):
        """
        Constructor
        
        @param item reference to the item
        @type QTreeWidgetItem
        @param column column number inside the tree widget item
        @type int
        """
        super(HgHisteditPlanActionComboBox, self).__init__()
        
        self.__item = item
        self.__column = column
        
        self.addItems(["pick", "drop", "mess", "fold", "roll", "edit"])
        txt = self.__item.text(self.__column)
        index = self.findText(txt)
        if index > -1:
            self.setCurrentIndex(index)
        
        self.currentIndexChanged.connect(self.__changeItem)
    
    @pyqtSlot(int)
    def __changeItem(self, index):
        """
        Private slot to handle the selection of a plan action.
        
        This method sets the text of the associated item for the specified
        cell in order to be able to retrieve it with a call of the text()
        method of the item.
        
        @param index index of the selected action
        @type int
        """
        self.__item.setText(self.__column, self.currentText())
        self.__item.treeWidget().setCurrentItem(self.__item)
    
    def showPopup(self):
        """
        Public method to show the list of items of the combo box.
        
        This is reimplemented in order to set the associated item as the
        current item of the tree widget.
        """
        self.__item.treeWidget().setCurrentItem(self.__item)
        super(HgHisteditPlanActionComboBox, self).showPopup()


class HgHisteditPlanEditor(QDialog, Ui_HgHisteditPlanEditor):
    """
    Class implementing a dialog to edit the history modification plan.
    """
    def __init__(self, fileName, parent=None):
        """
        Constructor
        
        @param fileName name of the file containing the history edit plan
            to be edited
        @type str
        @param parent reference to the parent widget
        @type QWidget
        """
        super(HgHisteditPlanEditor, self).__init__(parent)
        self.setupUi(self)
        
        self.upButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
        self.downButton.setIcon(UI.PixmapCache.getIcon("1downarrow"))
        
        self.planTreeWidget.headerItem().setText(
            self.planTreeWidget.columnCount(), "")
        
        self.__fileName = fileName
        self.__readFile()
        
        self.__updateButtons()
    
    def __readFile(self):
        """
        Private method to read the file containing the edit plan and
        populate the dialog.
        """
        try:
            f = open(self.__fileName, "r")
            txt = f.read()
            f.close()
        except (IOError, OSError) as err:
            E5MessageBox.critical(
                self,
                self.tr("Edit Plan"),
                self.tr("""<p>The file <b>{0}</b> could not be read.</p>"""
                        """<p>Reason: {1}</p>""").format(
                    self.__fileName, str(err)))
            self.on_buttonBox_rejected()
            return
        
        infoLines = []
        for line in txt.splitlines():
            if line.startswith("#"):
                infoLines.append(line[1:].lstrip())
            else:
                self.__createPlanItem(line)
        self.infoEdit.setPlainText("\n".join(infoLines))
        
        self.__resizeSections()
    
    def __addActionCombo(self, item):
        """
        Private method to add an edit action combo to an item.
        
        @param item reference to the tree widget item
        @type QTreeWidgetItem
        """
        actionCombo = HgHisteditPlanActionComboBox(item, 0)
        self.planTreeWidget.setItemWidget(item, 0, actionCombo)
        item.setSizeHint(0, actionCombo.sizeHint())
    
    def __createPlanItem(self, text):
        """
        Private method to create an edit plan tree item.
        
        @param text line of text to be parsed
        @type str
        """
        if not text.lstrip():
            return
        
        parts = text.split(" ", 3)
        action = parts[0]
        try:
            rev = int(parts[2])
            if len(parts) > 3:
                summary = parts[3]
            else:
                summary = ""
        except ValueError:
            rev = -1
            summary = " ".join(parts[2:])
        if rev > -1:
            revision = "{0:>7}:{1}".format(rev, parts[1])
        else:
            revision = parts[1]
        
        itm = QTreeWidgetItem(self.planTreeWidget, [
            action,
            revision,
            summary,
        ])
        self.__addActionCombo(itm)
    
    def __resizeSections(self):
        """
        Private method to resize the tree widget sections.
        """
        for column in range(self.planTreeWidget.columnCount()):
            self.planTreeWidget.resizeColumnToContents(column)
        self.planTreeWidget.header().setStretchLastSection(True)
    
    def __updateButtons(self):
        """
        Private method to set the enabled state of the up and down buttons.
        """
        if self.planTreeWidget.currentItem() is None:
            self.upButton.setEnabled(False)
            self.downButton.setEnabled(False)
        else:
            row = self.planTreeWidget.indexOfTopLevelItem(
                self.planTreeWidget.currentItem())
            self.upButton.setEnabled(row > 0)
            self.downButton.setEnabled(
                row < self.planTreeWidget.topLevelItemCount() - 1)
    
    @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
    def on_planTreeWidget_currentItemChanged(self, current, previous):
        """
        Private slot handling the change of the current edit plan item.
        
        @param current reference to the current edit plan item
        @type QTreeWidgetItem
        @param previous reference to the previous current edit plan item
        @type QTreeWidgetItem
        """
        self.__updateButtons()
    
    @pyqtSlot()
    def on_upButton_clicked(self):
        """
        Private slot to move the current entry up one line.
        """
        row = self.planTreeWidget.indexOfTopLevelItem(
            self.planTreeWidget.currentItem())
        if row > 0:
            targetRow = row - 1
            itm = self.planTreeWidget.takeTopLevelItem(row)
            self.planTreeWidget.insertTopLevelItem(targetRow, itm)
            self.__addActionCombo(itm)
            self.planTreeWidget.setCurrentItem(itm)
    
    @pyqtSlot()
    def on_downButton_clicked(self):
        """
        Private slot to move the current entry down one line.
        """
        row = self.planTreeWidget.indexOfTopLevelItem(
            self.planTreeWidget.currentItem())
        if row < self.planTreeWidget.topLevelItemCount() - 1:
            targetRow = row + 1
            itm = self.planTreeWidget.takeTopLevelItem(row)
            self.planTreeWidget.insertTopLevelItem(targetRow, itm)
            self.__addActionCombo(itm)
            self.planTreeWidget.setCurrentItem(itm)
    
    @pyqtSlot()
    def on_buttonBox_accepted(self):
        """
        Private slot called by the buttonBox accepted signal.
        """
        text = self.__assembleEditPlan()
        try:
            f = open(self.__fileName, "w")
            f.write(text)
            f.close()
        except (IOError, OSError) as err:
            E5MessageBox.critical(
                self,
                self.tr("Edit Plan"),
                self.tr("""<p>The file <b>{0}</b> could not be read.</p>"""
                        """<p>Reason: {1}</p>""").format(
                    self.__fileName, str(err)))
            self.on_buttonBox_rejected()
            return
        
        self.close()
        QCoreApplication.exit(0)
    
    @pyqtSlot()
    def on_buttonBox_rejected(self):
        """
        Private slot called by the buttonBox rejected signal.
        """
        self.close()
        QCoreApplication.exit(1)
    
    def __assembleEditPlan(self):
        """
        Private method to assemble the edit plan into text suitable for the
        histedit file.
        
        @return assembled edit plan text
        @rtype str
        """
        lines = []
        for row in range(self.planTreeWidget.topLevelItemCount()):
            itm = self.planTreeWidget.topLevelItem(row)
            if ":" in itm.text(1):
                rev, changeset = itm.text(1).split(":", 1)
                rev = "{0} {1}".format(changeset.strip(), rev.strip())
            else:
                rev = itm.text(1).strip()
            
            lines.append("{0} {1} {2}".format(itm.text(0).strip(), rev,
                                              itm.text(2)))
        
        return "\n".join(lines)
