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

##########################################################################
#                                                                        #
#  Eddy: a graphical editor for the specification of Graphol ontologies  #
#  Copyright (C) 2015 Daniele Pantaleone <danielepantaleone@me.com>      #
#                                                                        #
#  This program is free software: you can redistribute it and/or modify  #
#  it under the terms of the GNU General Public License as published by  #
#  the Free Software Foundation, either version 3 of the License, or     #
#  (at your option) any later version.                                   #
#                                                                        #
#  This program is distributed in the hope that it will be useful,       #
#  but WITHOUT ANY WARRANTY; without even the implied warranty of        #
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          #
#  GNU General Public License for more details.                          #
#                                                                        #
#  You should have received a copy of the GNU General Public License     #
#  along with this program. If not, see <http://www.gnu.org/licenses/>.  #
#                                                                        #
#  #####################                          #####################  #
#                                                                        #
#  Graphol is developed by members of the DASI-lab group of the          #
#  Dipartimento di Ingegneria Informatica, Automatica e Gestionale       #
#  A.Ruberti at Sapienza University of Rome: http://www.dis.uniroma1.it  #
#                                                                        #
#     - Domenico Lembo <lembo@dis.uniroma1.it>                           #
#     - Valerio Santarelli <santarelli@dis.uniroma1.it>                  #
#     - Domenico Fabio Savo <savo@dis.uniroma1.it>                       #
#     - Daniele Pantaleone <pantaleone@dis.uniroma1.it>                  #
#     - Marco Console <console@dis.uniroma1.it>                          #
#                                                                        #
##########################################################################


from operator import itemgetter

from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5 import QtWidgets

from eddy.core.common import HasWidgetSystem
from eddy.core.datatypes.qt import Font, PHCQPushButton
from eddy.core.functions.misc import isEmpty
from eddy.core.functions.signals import connect
from eddy.core.regex import RE_CAMEL_SPACE

from eddy.ui.fields import CheckBox, TextField


K_CURRENT = 'current'
K_FINAL = 'final'
K_IMPORTING = 'importing'
K_ITEM = 'item'
K_NAME = 'name'
K_PROPERTY = 'property'


class PredicateDocumentationConflictResolver(QtWidgets.QDialog, HasWidgetSystem):
    """
    This class is used to resolve conflicts generated by the different documentation of the same predicate.
    """
    def __init__(self, item, name, current, importing, parent=None):
        """
        Initialize the project dialog.
        :type item: Item
        :type name: str
        :type current: str
        :type importing: str
        :type parent: QWidget
        """
        super().__init__(parent)

        self.item = item
        self.name = name

        #############################################
        # LEFT SIDE
        #################################

        widget = QtWidgets.QLabel(self)
        widget.setAlignment(QtCore.Qt.AlignCenter)
        widget.setFont(Font('Roboto', 14, bold=True, capitalization=Font.AllUppercase))
        widget.setObjectName('current_title')
        widget.setText('Current')
        self.addWidget(widget)

        widget = TextField(self)
        widget.setFixedSize(300, 200)
        widget.setFocusPolicy(QtCore.Qt.NoFocus)
        widget.setFont(Font('Roboto', 12))
        widget.setObjectName('current_documentation')
        widget.setReadOnly(True)
        widget.setValue(current)
        self.addWidget(widget)

        widget = PHCQPushButton(self)
        widget.setAutoDefault(False)
        widget.setDefault(False)
        widget.setProperty('class', 'flat blue')
        widget.setFixedWidth(32)
        widget.setIcon(QtGui.QIcon(':/icons/24/ic_keyboard_arrow_right_black'))
        widget.setIconSize(QtCore.QSize(24, 24))
        widget.setObjectName('pick_current_button')
        widget.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)
        connect(widget.clicked, self.doPickDocumentation)
        self.addWidget(widget)

        # widget = QtWidgets.QLabel(self)
        # widget.setFixedSize(QtCore.QSize(24, 24))
        # widget.setPixmap(QtGui.QIcon(':/icons/24/ic_compare_arrows_black').pixmap(24))
        # widget.setObjectName('compare_arrows_current_icon')
        # self.addWidget(widget)

        #############################################
        # MIDDLE SIDE
        #################################

        widget = QtWidgets.QLabel(self)
        widget.setAlignment(QtCore.Qt.AlignCenter)
        widget.setFont(Font('Roboto', 14, bold=True, capitalization=Font.AllUppercase))
        widget.setObjectName('final_title')
        widget.setText('Final')
        self.addWidget(widget)

        widget = TextField(self)
        widget.setFixedSize(300, 200)
        widget.setFont(Font('Roboto', 12))
        widget.setObjectName('final_documentation')
        self.addWidget(widget)

        #############################################
        # RIGHT SIDE
        #################################

        widget = QtWidgets.QLabel(self)
        widget.setAlignment(QtCore.Qt.AlignCenter)
        widget.setFont(Font('Roboto', 14, bold=True, capitalization=Font.AllUppercase))
        widget.setObjectName('importing_title')
        widget.setText('Importing')
        self.addWidget(widget)

        widget = TextField(self)
        widget.setFixedSize(300, 200)
        widget.setFocusPolicy(QtCore.Qt.NoFocus)
        widget.setFont(Font('Roboto', 12))
        widget.setObjectName('importing_documentation')
        widget.setReadOnly(True)
        widget.setValue(importing)
        self.addWidget(widget)

        widget = PHCQPushButton(self)
        widget.setAutoDefault(False)
        widget.setDefault(False)
        widget.setProperty('class', 'flat blue')
        widget.setFixedWidth(32)
        widget.setIcon(QtGui.QIcon(':/icons/24/ic_keyboard_arrow_left_black'))
        widget.setIconSize(QtCore.QSize(24, 24))
        widget.setObjectName('pick_importing_button')
        widget.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)
        connect(widget.clicked, self.doPickDocumentation)
        self.addWidget(widget)

        # widget = QtWidgets.QLabel(self)
        # widget.setFixedSize(QtCore.QSize(24, 24))
        # widget.setPixmap(QtGui.QIcon(':/icons/24/ic_compare_arrows_black').pixmap(24))
        # widget.setObjectName('compare_arrows_importing_icon')
        # self.addWidget(widget)

        #############################################
        # CONFIRMATION AREA
        #################################

        widget = QtWidgets.QDialogButtonBox(QtCore.Qt.Horizontal, self)
        widget.addButton(QtWidgets.QDialogButtonBox.Ok)
        widget.addButton(QtWidgets.QDialogButtonBox.Abort)
        widget.setContentsMargins(0, 4, 0, 0)
        widget.setFont(Font('Roboto', 12))
        widget.setObjectName('confirmation_box')
        connect(widget.accepted, self.accept)
        connect(widget.rejected, self.reject)
        self.addWidget(widget)

        #############################################
        # SETUP DIALOG LAYOUT
        #################################

        gridWidget = QtWidgets.QWidget(self)
        gridLayout = QtWidgets.QGridLayout(gridWidget)
        gridLayout.setContentsMargins(0, 0, 0, 0)
        gridLayout.addWidget(self.widget('current_title'), 0, 0)
        gridLayout.addWidget(self.widget('current_documentation'), 1, 0)
        gridLayout.addWidget(self.widget('final_title'), 0, 2)
        gridLayout.addWidget(self.widget('final_documentation'), 1, 2)
        gridLayout.addWidget(self.widget('importing_title'), 0, 4)
        gridLayout.addWidget(self.widget('importing_documentation'), 1, 4)
        # gridLayout.addWidget(self.widget('compare_arrows_current_icon'), 0, 1, QtCore.Qt.AlignCenter)
        # gridLayout.addWidget(self.widget('compare_arrows_importing_icon'), 0, 3, QtCore.Qt.AlignCenter)
        gridLayout.addWidget(self.widget('pick_current_button'), 1, 1)
        gridLayout.addWidget(self.widget('pick_importing_button'), 1, 3)

        mainLayout = QtWidgets.QVBoxLayout()
        mainLayout.addWidget(gridWidget)
        mainLayout.addWidget(self.widget('confirmation_box'))
        mainLayout.setContentsMargins(10, 10, 10, 10)

        self.setLayout(mainLayout)
        self.setFixedSize(self.sizeHint())
        self.setFont(Font('Roboto', 12))
        self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy'))
        self.setWindowTitle("Resolve documentation conflict for {0} '{1}'...".format(self.item.shortName, self.name))

    #############################################
    #   INTERFACE
    #################################

    def result(self):
        """
        Returns the chosen documentation string.
        :rtype: str
        """
        return self.widget('final_documentation').value()

    #############################################
    #   SLOTS
    #################################

    @QtCore.pyqtSlot()
    def accept(self):
        """
        Accepts the conflict resolution form.
        """
        source = self.widget('final_documentation')
        if isEmpty(source.value()):
            msgbox = QtWidgets.QMessageBox(self)
            msgbox.setIconPixmap(QtGui.QIcon(':/icons/48/ic_warning_black').pixmap(48))
            msgbox.setStandardButtons(QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes)
            msgbox.setText("No documentation specified for {0} '{1}'. "
                           "Do you want to continue?".format(self.item.shortName, self.name))
            msgbox.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy'))
            msgbox.setWindowTitle('No documentation specified!')
            msgbox.exec_()
            if msgbox.result() == QtWidgets.QMessageBox.No:
                return
        super().accept()

    @QtCore.pyqtSlot()
    def doPickDocumentation(self):
        """
        Executed when a pick button is clicked.
        """
        source = self.widget('current_documentation')
        if self.sender() is self.widget('pick_importing_button'):
            source = self.widget('importing_documentation')
        target = self.widget('final_documentation')
        target.setValue(source.value())


class PredicateBooleanConflictResolver(QtWidgets.QDialog, HasWidgetSystem):
    """
    This class is used to resolve conflicts generated by the different boolean properties of predicates.
    """
    def __init__(self, collection, parent=None):
        """
        Initialize the project dialog.
        :type collection: T <= list | tuple
        :type parent: QWidget
        """
        super().__init__(parent)

        self.collection = sorted(collection, key=itemgetter(K_ITEM, K_NAME))

        #############################################
        # CHECKBOX WIDGETS
        #################################

        self.marks = {K_CURRENT: {}, K_IMPORTING: {}, K_FINAL: {}}

        for e in self.collection:
            ## CREATE CURRENT CHECKBOX
            mark = CheckBox(self)
            mark.setEnabled(False)
            mark.setChecked(e[K_CURRENT])
            if e[K_ITEM] not in self.marks[K_CURRENT]:
                self.marks[K_CURRENT][e[K_ITEM]] = {}
            if e[K_NAME] not in self.marks[K_CURRENT][e[K_ITEM]]:
                self.marks[K_CURRENT][e[K_ITEM]][e[K_NAME]] = {}
            self.marks[K_CURRENT][e[K_ITEM]][e[K_NAME]][e[K_PROPERTY]] = mark
            ## CREATE IMPORTING CHECKBOX
            mark = CheckBox(self)
            mark.setEnabled(False)
            mark.setChecked(e[K_IMPORTING])
            if e[K_ITEM] not in self.marks[K_IMPORTING]:
                self.marks[K_IMPORTING][e[K_ITEM]] = {}
            if e[K_NAME] not in self.marks[K_IMPORTING][e[K_ITEM]]:
                self.marks[K_IMPORTING][e[K_ITEM]][e[K_NAME]] = {}
            self.marks[K_IMPORTING][e[K_ITEM]][e[K_NAME]][e[K_PROPERTY]] = mark
            ## CREATE FINAL CHECKBOX
            mark = CheckBox(self)
            mark.setEnabled(True)
            mark.setTristate(True)
            mark.setCheckState(QtCore.Qt.PartiallyChecked)
            connect(mark.stateChanged, self.onMarkStateChanged)
            if e[K_ITEM] not in self.marks[K_FINAL]:
                self.marks[K_FINAL][e[K_ITEM]] = {}
            if e[K_NAME] not in self.marks[K_FINAL][e[K_ITEM]]:
                self.marks[K_FINAL][e[K_ITEM]][e[K_NAME]] = {}
            self.marks[K_FINAL][e[K_ITEM]][e[K_NAME]][e[K_PROPERTY]] = mark

        #############################################
        # BUTTONS & PLACEHOLDERS
        #################################

        # widget = QtWidgets.QLabel(self)
        # widget.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
        # widget.setFont(Font('Roboto', 14, bold=True, capitalization=Font.AllUppercase))
        # widget.setObjectName('title_type')
        # widget.setText('Type')
        # self.addWidget(widget)

        # widget = QtWidgets.QLabel(self)
        # widget.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
        # widget.setFont(Font('Roboto', 14, bold=True, capitalization=Font.AllUppercase))
        # widget.setObjectName('title_name')
        # widget.setText('Name')
        # self.addWidget(widget)

        # widget = QtWidgets.QLabel(self)
        # widget.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
        # widget.setFont(Font('Roboto', 14, bold=True, capitalization=Font.AllUppercase))
        # widget.setObjectName('title_property')
        # widget.setText('Property')
        # self.addWidget(widget)

        widget = QtWidgets.QLabel(self)
        widget.setAlignment(QtCore.Qt.AlignCenter)
        widget.setFont(Font('Roboto', 14, bold=True, capitalization=Font.AllUppercase))
        widget.setObjectName('title_current')
        widget.setText('Current')
        self.addWidget(widget)

        widget = QtWidgets.QLabel(self)
        widget.setAlignment(QtCore.Qt.AlignCenter)
        widget.setFont(Font('Roboto', 14, bold=True, capitalization=Font.AllUppercase))
        widget.setObjectName('title_final')
        widget.setText('Final')
        self.addWidget(widget)

        widget = QtWidgets.QLabel(self)
        widget.setAlignment(QtCore.Qt.AlignCenter)
        widget.setFont(Font('Roboto', 14, bold=True, capitalization=Font.AllUppercase))
        widget.setObjectName('title_importing')
        widget.setText('Importing')
        self.addWidget(widget)

        # widget = QtWidgets.QLabel(self)
        # widget.setFixedSize(QtCore.QSize(24, 24))
        # widget.setPixmap(QtGui.QIcon(':/icons/24/ic_compare_arrows_black').pixmap(24))
        # widget.setObjectName('compare_arrows_current_icon')
        # self.addWidget(widget)

        # widget = QtWidgets.QLabel(self)
        # widget.setFixedSize(QtCore.QSize(24, 24))
        # widget.setPixmap(QtGui.QIcon(':/icons/24/ic_compare_arrows_black').pixmap(24))
        # widget.setObjectName('compare_arrows_importing_icon')
        # self.addWidget(widget)

        widget = PHCQPushButton(self)
        widget.setAutoDefault(False)
        widget.setDefault(False)
        widget.setProperty('class', 'flat blue')
        widget.setFixedWidth(32)
        widget.setIcon(QtGui.QIcon(':/icons/24/ic_keyboard_arrow_right_black'))
        widget.setIconSize(QtCore.QSize(24, 24))
        widget.setObjectName('pick_current_button')
        widget.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)
        connect(widget.clicked, self.doPickBooleanSet)
        self.addWidget(widget)

        widget = PHCQPushButton(self)
        widget.setAutoDefault(False)
        widget.setDefault(False)
        widget.setProperty('class', 'flat blue')
        widget.setFixedWidth(32)
        widget.setIcon(QtGui.QIcon(':/icons/24/ic_keyboard_arrow_left_black'))
        widget.setIconSize(QtCore.QSize(24, 24))
        widget.setObjectName('pick_importing_button')
        widget.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)
        connect(widget.clicked, self.doPickBooleanSet)
        self.addWidget(widget)

        #############################################
        # CONFIRMATION AREA
        #################################

        widget = QtWidgets.QDialogButtonBox(QtCore.Qt.Horizontal, self)
        widget.addButton(QtWidgets.QDialogButtonBox.Ok)
        widget.addButton(QtWidgets.QDialogButtonBox.Abort)
        widget.setContentsMargins(0, 4, 0, 0)
        widget.setFont(Font('Roboto', 12))
        widget.setObjectName('confirmation_box')
        widget.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
        connect(widget.accepted, self.accept)
        connect(widget.rejected, self.reject)
        self.addWidget(widget)

        #############################################
        # SETUP DIALOG LAYOUT
        #################################

        gridWidget = QtWidgets.QWidget(self)
        gridLayout = QtWidgets.QGridLayout(gridWidget)
        gridLayout.setContentsMargins(0, 0, 0, 0)
        #gridLayout.addWidget(self.widget('title_type'), 0, 0)
        #gridLayout.addWidget(self.widget('title_name'), 0, 1)
        #gridLayout.addWidget(self.widget('title_property'), 0, 2)
        gridLayout.addWidget(self.widget('title_current'), 0, 3)
        gridLayout.addWidget(self.widget('title_final'), 0, 5)
        gridLayout.addWidget(self.widget('title_importing'), 0, 7)
        #gridLayout.addWidget(self.widget('compare_arrows_current_icon'), 0, 4, QtCore.Qt.AlignCenter)
        #gridLayout.addWidget(self.widget('compare_arrows_importing_icon'), 0, 6, QtCore.Qt.AlignCenter)

        for row, e in enumerate(self.collection, start=1):
            w_type = QtWidgets.QLabel(self)
            w_type.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
            w_type.setFont(Font('Roboto', 12))
            w_type.setText(e[K_ITEM].shortName.title())
            w_name = QtWidgets.QLabel(self)
            w_name.setContentsMargins(40, 0, 40, 0)
            w_name.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
            w_name.setFont(Font('Roboto', 12, italic=True))
            w_name.setText(e[K_NAME])
            w_prop = QtWidgets.QLabel(self)
            w_prop.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
            w_prop.setFont(Font('Roboto', 14, bold=True, capitalization=Font.SmallCaps))
            w_prop.setText(RE_CAMEL_SPACE.sub('\g<1> \g<2>', e[K_PROPERTY]).lower())
            gridLayout.addWidget(w_type, row, 0)
            gridLayout.addWidget(w_name, row, 1)
            gridLayout.addWidget(w_prop, row, 2)
            gridLayout.addWidget(self.marks[K_CURRENT][e[K_ITEM]][e[K_NAME]][e[K_PROPERTY]], row, 3, QtCore.Qt.AlignCenter)
            gridLayout.addWidget(self.marks[K_FINAL][e[K_ITEM]][e[K_NAME]][e[K_PROPERTY]], row, 5, QtCore.Qt.AlignCenter)
            gridLayout.addWidget(self.marks[K_IMPORTING][e[K_ITEM]][e[K_NAME]][e[K_PROPERTY]], row, 7, QtCore.Qt.AlignCenter)

        gridLayout.addWidget(self.widget('pick_current_button'), 1, 4, len(self.collection), 1)
        gridLayout.addWidget(self.widget('pick_importing_button'), 1, 6, len(self.collection), 1)

        mainLayout = QtWidgets.QVBoxLayout()
        mainLayout.addWidget(gridWidget)
        mainLayout.addWidget(self.widget('confirmation_box'))
        mainLayout.setContentsMargins(10, 10, 10, 10)

        self.setLayout(mainLayout)
        self.setFixedSize(self.sizeHint())
        self.setFont(Font('Roboto', 12))
        self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy'))
        self.setWindowTitle("Resolve conflicts...")

    #############################################
    #   INTERFACE
    #################################

    def results(self):
        """
        Returns the conflict resolution.
        :rtype: tuple
        """
        return ({
            K_ITEM: e[K_ITEM],
            K_NAME: e[K_NAME],
            K_PROPERTY: e[K_PROPERTY],
            K_FINAL: self.marks[K_FINAL][e[K_ITEM]][e[K_NAME]][e[K_PROPERTY]].isChecked()
        } for e in self.collection)

    #############################################
    #   SLOTS
    #################################

    @QtCore.pyqtSlot(int)
    def onMarkStateChanged(self, _):
        """
        Executed whenever the state of a checkbox changes.
        :type _: int
        """
        f_mark = self.sender()
        f_mark.setTristate(False)
        widget = self.widget('confirmation_box')
        widget.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(
            not any(self.marks[K_FINAL][i][n][p].checkState() == QtCore.Qt.PartiallyChecked
                for i in self.marks[K_FINAL]
                    for n in self.marks[K_FINAL][i]
                        for p in self.marks[K_FINAL][i][n]))

    @QtCore.pyqtSlot()
    def doPickBooleanSet(self):
        """
        Executed when a pick button is clicked.
        """
        source = K_CURRENT
        if self.sender() is self.widget('pick_importing_button'):
            source = K_IMPORTING
        for entry in self.collection:
            s_mark = self.marks[source][entry[K_ITEM]][entry[K_NAME]][entry[K_PROPERTY]]
            f_mark = self.marks[K_FINAL][entry[K_ITEM]][entry[K_NAME]][entry[K_PROPERTY]]
            f_mark.setCheckState(QtCore.Qt.Checked if s_mark.isChecked() else QtCore.Qt.Unchecked)
            f_mark.setTristate(False)