# -*- coding: utf-8 -*-
#
# Copyright 2016 Continuum Analytics, Inc.
# May be copied and distributed freely only as part of an Anaconda or
# Miniconda installation.
#

"""
"""

# Third party imports
from qtpy.QtCore import Qt, Signal, QSize, QRegExp
from qtpy.QtGui import QRegExpValidator
from qtpy.QtWidgets import (QHBoxLayout, QLabel, QLineEdit, QListWidget,
                            QListWidgetItem, QMessageBox, QPushButton,
                            QStyledItemDelegate, QVBoxLayout, QWidget)
import qtawesome as qta


class EditableListDelegate(QStyledItemDelegate):
    """
    Delegate to control the input of a row inside an editable list.
    """
    def __init__(self, regex=None, tooltip=None):
        super(EditableListDelegate, self).__init__()
        self.regex = regex
        self.qregex = QRegExp(regex)
        self.tooltip = tooltip
        self.validator = QRegExpValidator(self.qregex)
        self.edit = None

    def createEditor(self, parent, option, index):
        edit = QLineEdit(parent)
        edit.setToolTip(self.tooltip)
        if self.regex:
            edit.setValidator(self.validator)
        return edit


class EditableList(QWidget):
    """
    Editable list with label, add, delete and edit buttons.
    """
    sig_item_edited = Signal(object)
    sig_item_removed = Signal(object)
    sig_item_selected = Signal()

    def __init__(self, title=None, items_text=[], duplicates=False,
                 normalize=True, min_items=1, confirm_remove=True,
                 regex=None, tooltip=None):
        super(EditableList, self).__init__()

        self.duplicates = duplicates
        self.items_text = items_text
        self.normalize = normalize
        self.min_items = min_items
        self.confirm_remove = confirm_remove

        # Widgets
        self.label_title = QLabel(title)
        self.button_add = QPushButton(qta.icon('fa.plus'), '')
        self.button_remove = QPushButton(qta.icon('fa.minus'), '')
        self.button_edit = QPushButton(qta.icon('fa.edit'), '')
        self.list = QListWidget()
        self.delegate = EditableListDelegate(regex=regex,
                                             tooltip=tooltip)

        # Widget setup
        self.list.setItemDelegate(self.delegate)
        self.setMaximumHeight(self._height()*4)

        # Layout
        label_buttons_layout = QHBoxLayout()
        label_buttons_layout.addWidget(self.label_title, 0)
        label_buttons_layout.addStretch()
        label_buttons_layout.addWidget(self.button_add)
        label_buttons_layout.addWidget(self.button_remove)
        label_buttons_layout.addWidget(self.button_edit)

        layout = QVBoxLayout()
        layout.addLayout(label_buttons_layout)
        layout.addWidget(self.list)

        self.setLayout(layout)

        # Signals
        self.button_add.clicked.connect(self.add)
        self.button_edit.clicked.connect(self.edit)
        self.button_remove.clicked.connect(self.remove)

        self.delegate.closeEditor.connect(self.check_value)
        self.list.currentItemChanged.connect(self.refresh)
        self.list.itemSelectionChanged.connect(self.sig_item_selected)

        # Setup
        self.setup()

        # Expose list methods
        self.clear = self.list.clear
        self.currentItem = self.list.currentItem
        self.setCurrentRow = self.list.setCurrentRow
        self.currentRow = self.list.currentRow
        self.item = self.list.item
        self.count = self.list.count

    def _height(self):
        """
        Get the height for the row in the widget based on OS font metrics.
        """
        return self.fontMetrics().height()*2

    def setup(self):
        """
        Initial setup for populating items if any.
        """
        # TODO: Check against regex and raise error accordingly!
        new_items = []
        for text in self.items_text:
            if self.normalize:
                text = text.lower()
            new_items.append(text)

        self.items_text = new_items

        if not self.duplicates:
            if len(set(self.items_text)) != len(self.items_text):
                raise Exception('The list cannot contains duplicates.')

        for item in self.items_text:
            item = QListWidgetItem(item)
            item.extra_data = None
            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
            self.add(item)
            item.setSizeHint(QSize(item.sizeHint().width(), self._height()))

        self.refresh()

    def get_texts(self):
        """
        Returns the list of texts in the list, excluding the ones under
        edition.
        """
        row = self.list.currentRow()
        texts = []
        for i in range(self.list.count()):
            item = self.list.item(i)
            if item:
                text = item.text().lower() if self.normalize else item.text()
                texts.append(text)

        # Check for duplicates. But the entered text already is part of the
        # items, so that needs to be removed to make the check
        if texts and row != -1:
            texts.pop(row)

        return texts

    def add(self, item=None):
        """
        Return the text of all items in the list, except the current one being
        edited.
        """
        if item:
            if item.text() in self.get_texts() and not self.duplicates:
                raise Exception
            else:
                self.list.addItem(item)
        else:
            item = QListWidgetItem()
            item.extra_data = None
            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable |
                          Qt.ItemIsEditable)
            self.list.addItem(item)
            self.list.setCurrentItem(item)
            item.setSizeHint(QSize(item.sizeHint().width(), self._height()))
            self.edit()
        self.refresh()

    def remove(self):
        """
        Remove item fron the list.
        """
        row = self.list.currentRow()
        item = self.list.currentItem()

        if item is None:
            self.refresh()
        else:
            text = item.text()

        if self.confirm_remove:
            message = ("Are you sure you want to remove<br>"
                       "<strong>{0}</strong>?".format(text))
            reply = QMessageBox.question(self, 'Remove item', message,
                                         QMessageBox.Yes, QMessageBox.No)
            remove_item = reply == QMessageBox.Yes
        else:
            remove_item = True

        if row != -1 and remove_item:
            item = self.list.takeItem(row)
            self.sig_item_removed.emit(item)

        self.refresh()

    def edit(self):
        """
        Start editing item from the list.
        """
        self.button_remove.setDisabled(True)
        self.button_add.setDisabled(True)
        self.button_edit.setDisabled(True)
        item = self.current_item()
        if item:
            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable |
                          Qt.ItemIsEditable)
            item._text = item.text()
            self.list.editItem(item)

    def current_item(self):
        """
        Return the current selected item.
        """
        item = None
        row = self.list.currentRow()
        if row != -1:
            item = self.list.item(row)
        return item

    def check_value(self):
        """
        After editing an item check the value is valid and return to edit mode
        if it does not pass, or accept the value and add it.
        """
        texts = self.get_texts()
        item = self.current_item()
        self._temp_item = item
        if item:
            text = item.text().lower() if self.normalize else item.text()
            row = self.list.currentRow()
            if text.strip() == '':
                self.list.takeItem(row)
            if text.strip() in texts and not self.duplicates:
                self.edit()
            else:
                self.sig_item_edited.emit(item)
            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)

    def refresh(self):
        """
        Refresh the enabled status of the actions buttons.
        """
        current_row = self.list.currentRow()

        if self.list.count() == 0:
            self.button_edit.setDisabled(True)
            self.button_remove.setDisabled(True)

        elif self.list.count() != 0 and current_row == -1:
            self.button_edit.setDisabled(True)
            self.button_remove.setDisabled(True)

        elif self.list.count() == self.min_items:
            self.button_add.setDisabled(False)
            self.button_edit.setDisabled(False)
            self.button_remove.setDisabled(True)

        elif self.list.count() != 0 and current_row != -1:
            self.button_add.setDisabled(False)
            self.button_edit.setDisabled(False)
            self.button_remove.setDisabled(False)

        for i in range(self.list.count()):
            item = self.list.item(i)
            item.setSizeHint(QSize(item.sizeHint().width(), self._height()))


def test_print():
    print('selection changed')


def test():
    from anaconda_navigator.utils.qthelpers import qapplication
    app = qapplication()
    regex = '^[a-zA-Z0-9_\-]{1,20}'
    w = EditableList(title='Environments', items_text=['item-1', 'item-2'],
                     normalize=True, regex=regex,
                     tooltip='Enter the name of the environment')
    w.sig_item_selected.connect(test_print)
    w.show()
    app.exec_()


if __name__ == '__main__':
    test()
