# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)

"""Layout dialogs"""

# Standard library imports
import sys

# Third party imports
from qtpy.QtCore import QAbstractTableModel, QModelIndex, QSize, Qt
from qtpy.compat import from_qvariant, to_qvariant
from qtpy.QtWidgets import (
    QAbstractItemView,
    QDialog,
    QDialogButtonBox,
    QHBoxLayout,
    QMessageBox,
    QLabel,
    QTableView,
    QVBoxLayout,
)

# Local imports
from spyder.api.translations import _
from spyder.api.widgets.mixins import SpyderWidgetMixin
from spyder.api.widgets.comboboxes import SpyderComboBox
from spyder.api.widgets.dialogs import SpyderDialogButtonBox
from spyder.utils.stylesheet import AppStyle, PANES_TOOLBAR_STYLESHEET


class LayoutSettingsToolButtons:
    MoveUp = 'move_up'
    MoveDown = 'move_down'
    Remove = 'remove'


class LayoutModel(QAbstractTableModel):
    """ """
    def __init__(self, parent, names, ui_names, order, active, read_only):
        super().__init__(parent)

        # variables
        self._parent = parent
        self.names = names
        self.ui_names = ui_names
        self.order = order
        self.active = active
        self.read_only = read_only
        self._rows = []
        self.set_data(names, ui_names, order, active, read_only)

    def set_data(self, names, ui_names, order, active, read_only):
        """ """
        self._rows = []
        self.names = names
        self.ui_names = ui_names
        self.order = order
        self.active = active
        self.read_only = read_only
        for name in order:
            index = names.index(name)
            if name in active:
                row = [ui_names[index], name, True]
            else:
                row = [ui_names[index], name, False]
            self._rows.append(row)

    def flags(self, index):
        """Override Qt method"""
        row = index.row()
        ui_name, name, state = self.row(row)

        if name in self.read_only:
            return Qt.ItemFlag(0)
        if not index.isValid():
            return Qt.ItemFlag.ItemIsEnabled
        column = index.column()
        if column in [0]:
            return (Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable |
                    Qt.ItemFlag.ItemIsUserCheckable |
                    Qt.ItemFlag.ItemIsEditable)
        else:
            return Qt.ItemFlag.ItemIsEnabled

    def data(self, index, role=Qt.DisplayRole):
        """Override Qt method"""
        if not index.isValid() or not 0 <= index.row() < len(self._rows):
            return to_qvariant()
        row = index.row()
        column = index.column()

        ui_name, name, state = self.row(row)

        if role == Qt.DisplayRole or role == Qt.EditRole:
            if column == 0:
                return to_qvariant(ui_name)
        elif role == Qt.UserRole:
            if column == 0:
                return to_qvariant(name)
        elif role == Qt.CheckStateRole:
            if column == 0:
                if state:
                    return Qt.Checked
                else:
                    return Qt.Unchecked
            if column == 1:
                return to_qvariant(state)
        return to_qvariant()

    def setData(self, index, value, role):
        """Override Qt method"""
        row = index.row()
        ui_name, name, state = self.row(row)

        if role == Qt.CheckStateRole:
            self.set_row(row, [ui_name, name, not state])
            self._parent.setCurrentIndex(index)
            self._parent.setFocus()
            self.dataChanged.emit(index, index)
            return True
        elif role == Qt.EditRole:
            self.set_row(row, [from_qvariant(value, str), name, state])
            self.dataChanged.emit(index, index)
            return True
        return True

    def rowCount(self, index=QModelIndex()):
        """Override Qt method"""
        return len(self._rows)

    def columnCount(self, index=QModelIndex()):
        """Override Qt method"""
        return 2

    def row(self, rownum):
        """ """
        if self._rows == [] or rownum >= len(self._rows):
            return [None, None, None]
        else:
            return self._rows[rownum]

    def set_row(self, rownum, value):
        """ """
        self._rows[rownum] = value


class LayoutSaveDialog(QDialog):
    """Dialog to save a custom layout with a given name."""

    def __init__(self, parent, order):
        super().__init__(parent)

        # variables
        self._parent = parent

        # widgets
        self.combo_box = SpyderComboBox(self)
        self.combo_box.addItems(order)
        self.combo_box.setEditable(True)
        self.combo_box.clearEditText()
        self.combo_box.lineEdit().setPlaceholderText(
            _("Give a name to your layout")
        )

        self.button_box = SpyderDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self
        )
        self.button_ok = self.button_box.button(QDialogButtonBox.Ok)
        self.button_cancel = self.button_box.button(QDialogButtonBox.Cancel)

        # widget setup
        self.button_ok.setEnabled(False)
        self.dialog_size = QSize(350, 100)
        self.setWindowTitle(_('Save layout as'))
        self.setModal(True)
        self.setMinimumSize(self.dialog_size)
        self.setFixedSize(self.dialog_size)

        # layouts
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.combo_box)
        self.layout.addWidget(self.button_box)
        self.setLayout(self.layout)

        # signals and slots
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.close)
        self.combo_box.editTextChanged.connect(self.check_text)

    def check_text(self, text):
        """Disable empty layout name possibility"""
        if str(text) == '':
            self.button_ok.setEnabled(False)
        else:
            self.button_ok.setEnabled(True)


class LayoutSettingsDialog(QDialog, SpyderWidgetMixin):
    """Layout settings dialog"""

    # Dummy variable to avoid a warning
    CONF_SECTION = ""

    def __init__(self, parent, names, ui_names, order, active, read_only):
        super().__init__(parent)
        # variables
        self._parent = parent
        self._selection_model = None
        self.names = names
        self.ui_names = ui_names
        self.order = order
        self.active = active
        self.read_only = read_only

        # To style the tool buttons
        self.setStyleSheet(PANES_TOOLBAR_STYLESHEET.to_string())

        # widgets
        description_label = QLabel(_("Reorder or delete your saved layouts"))
        description_label.setAlignment(Qt.AlignCenter)
        description_label.setWordWrap(True)

        self.button_move_up = self.create_toolbutton(
            LayoutSettingsToolButtons.MoveUp,
            tip=_('Move up'),
            icon=self.create_icon('1uparrow'),
            triggered=lambda: self.move_layout(True),
            register=False,
        )
        self.button_move_down = self.create_toolbutton(
            LayoutSettingsToolButtons.MoveDown,
            tip=_('Move down'),
            icon=self.create_icon('1downarrow'),
            triggered=lambda: self.move_layout(False),
            register=False,
        )
        self.button_delete = self.create_toolbutton(
            LayoutSettingsToolButtons.Remove,
            tip=_('Delete layout'),
            icon=self.create_icon('editclear'),
            triggered=self.delete_layout,
            register=False,
        )

        self.button_box = SpyderDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self
        )

        self.table = QTableView(self)

        # widget setup
        self.dialog_size = QSize(320, 350)
        self.setMinimumSize(self.dialog_size)
        self.setFixedSize(self.dialog_size)
        self.setWindowTitle(_("Layouts display and order"))

        cancel_button = self.button_box.button(QDialogButtonBox.Cancel)
        cancel_button.setDefault(True)
        cancel_button.setAutoDefault(True)

        self.table.setModel(
            LayoutModel(self.table, names, ui_names, order, active, read_only))
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.setSelectionMode(QAbstractItemView.SingleSelection)
        self.table.verticalHeader().hide()
        self.table.horizontalHeader().hide()
        self.table.setAlternatingRowColors(True)
        self.table.setShowGrid(False)
        self.table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.table.horizontalHeader().setStretchLastSection(True)
        self.table.setColumnHidden(1, True)

        # need to keep a reference for pyside not to segfault!
        self._selection_model = self.table.selectionModel()

        # layout
        buttons_layout = QVBoxLayout()
        buttons_layout.addWidget(self.button_move_up)
        buttons_layout.addWidget(self.button_move_down)
        buttons_layout.addStretch()
        buttons_layout.addWidget(self.button_delete)

        order_layout = QHBoxLayout()
        order_layout.addWidget(self.table)
        order_layout.addLayout(buttons_layout)

        layout = QVBoxLayout()
        layout.addWidget(description_label)
        layout.addSpacing(2 * AppStyle.MarginSize)
        layout.addLayout(order_layout)
        layout.addSpacing(2 * AppStyle.MarginSize)
        layout.addWidget(self.button_box)

        self.setLayout(layout)

        # signals and slots
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.close)
        self.table.model().dataChanged.connect(
           lambda: self.selection_changed(None, None))
        self._selection_model.selectionChanged.connect(
           lambda: self.selection_changed(None, None))

        # focus table
        if len(names) > len(read_only):
            row = len(read_only)
            index = self.table.model().index(row, 0)
            self.table.setCurrentIndex(index)
            self.table.setFocus()
        else:
            # initial button state in case only programmatic layouts
            # are available
            self.button_move_up.setDisabled(True)
            self.button_move_down.setDisabled(True)
            self.button_delete.setDisabled(True)

    def delete_layout(self):
        """Delete layout from the config."""
        names, ui_names, order, active, read_only = (
            self.names, self.ui_names, self.order, self.active, self.read_only
        )
        row = self.table.selectionModel().currentIndex().row()
        ui_name, name, state = self.table.model().row(row)

        answer = QMessageBox.question(
            self,
            _("Remove layout"),
            _("Do you want to remove the layout '<b>{}</b>'?").format(ui_name),
            QMessageBox.Yes | QMessageBox.No
        )

        if not answer:
            return

        if name not in read_only:
            if ui_name in ui_names:
                index = ui_names.index(ui_name)
            else:
                # In case nothing has focus in the table
                return

            if index != -1:
                order.remove(name)
                names.remove(name)
                ui_names.remove(ui_name)

                if name in active:
                    active.remove(name)

                self.names, self.ui_names, self.order, self.active = (
                    names, ui_names, order, active
                )
                self.table.model().set_data(
                    names, ui_names, order, active, read_only
                )

                index = self.table.model().index(0, 0)
                self.table.setCurrentIndex(index)
                self.table.setFocus()
                self.selection_changed(None, None)
                if len(order) == 0 or len(names) == len(read_only):
                    self.button_move_up.setDisabled(True)
                    self.button_move_down.setDisabled(True)
                    self.button_delete.setDisabled(True)

    def move_layout(self, up=True):
        """ """
        names, ui_names, order, active, read_only = (
            self.names, self.ui_names, self.order, self.active, self.read_only)
        row = self.table.selectionModel().currentIndex().row()
        row_new = row
        _ui_name, name, _state = self.table.model().row(row)

        if name not in read_only:
            if up:
                row_new -= 1
            else:
                row_new += 1

            if order[row_new] not in read_only:
                order[row], order[row_new] = order[row_new], order[row]

                self.order = order
                self.table.model().set_data(
                    names, ui_names, order, active, read_only)
                index = self.table.model().index(row_new, 0)
                self.table.setCurrentIndex(index)
                self.table.setFocus()
                self.selection_changed(None, None)

    def selection_changed(self, selection, deselection):
        """ """
        model = self.table.model()
        index = self.table.currentIndex()
        row = index.row()
        order, names, ui_names, active, read_only = (
            self.order, self.names, self.ui_names, self.active, self.read_only)

        state = model.row(row)[2]
        ui_name = model.row(row)[0]

        # Check if name changed
        if ui_name not in ui_names:  # Did changed
            # row == -1, means no items left to delete
            if row != -1 and len(names) > len(read_only):
                old_name = order[row]
                order[row] = ui_name
                names[names.index(old_name)] = ui_name
                ui_names = names
                if old_name in active:
                    active[active.index(old_name)] = ui_name

        # Check if checkbox clicked
        if state:
            if ui_name not in active:
                active.append(ui_name)
        else:
            if ui_name in active:
                active.remove(ui_name)

        self.active = active
        self.order = order
        self.names = names
        self.ui_names = ui_names
        self.button_move_up.setDisabled(False)
        self.button_move_down.setDisabled(False)

        if row == 0:
            self.button_move_up.setDisabled(True)
        if row == len(names) - 1:
            self.button_move_down.setDisabled(True)
        if len(names) == 0:
            self.button_move_up.setDisabled(True)
            self.button_move_down.setDisabled(True)


def test():
    """Run layout test widget test"""
    from spyder.utils.qthelpers import qapplication

    app = qapplication()
    names = ['test', 'tester', '20', '30', '40']
    ui_names = ['L1', 'L2', '20', '30', '40']
    order = ['test', 'tester', '20', '30', '40']
    read_only = ['test', 'tester']
    active = ['test', 'tester']
    widget_1 = LayoutSettingsDialog(
        None, names, ui_names, order, active, read_only)
    widget_2 = LayoutSaveDialog(None, order)
    widget_1.show()
    widget_2.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    test()
