# Copyright (C) 2009 Canonical Ltd
#
# 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 2 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, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA


from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import Qt, QVariant, QModelIndex


class QBzrConditionalDataView(QtGui.QFrame):
    """A list/table/tree with a label.
    
    Only the label is shown when the data model is empty.
    """

    def __init__(self, type, listmode_or_headers, label_text,
            details, parent=None):
        """Construct the view.

        :param type: one of list, table, tree
        :param listmode_or_headers: For lists, set the initial list view
          mode: True => list, False => icon.
          For tables and trees, the list of headers.
        :param label_text: text for label. May contain %(rows)d to substitute
          the row count.
        :param details: if non-None, a QWidget to show in a details panel.
        :param parent: parent widget
        """
        QtGui.QFrame.__init__(self, parent)
        self._data = []

        # Build the model & view for the data
        self._type = type
        columns = listmode_or_headers
        if type == 'list':
            self._view = QtGui.QListView()
            self._view.setResizeMode(QtGui.QListView.Adjust)
            self._view.setWrapping(True)
            if listmode_or_headers:
                self._view.setViewMode(QtGui.QListView.ListMode)
            else:
                self._view.setViewMode(QtGui.QListView.IconMode)
            columns = ['Name']
            # TODO: we could add a combo-box here letting the user decide
            # on list vs icons. Would we need a way to switch it off?
        elif type == 'tree':
            self._view = QtGui.QTreeView()
        elif type == 'table':
            self._view = QtGui.QTableView()
        self._model = _TupleListModel()
        if type != 'list':
            self._model.setHorizontalHeaderLabels(columns)
        # Make the view read-only, and enable multi-selection of items
        self._view.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
        self._view.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
        self._view.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
        self._view.setModel(self._model)

        # Build the label
        self._label_text = label_text
        if label_text:
            self._label = QtGui.QLabel()
            self._update_label_text()

        # Put them together
        layout = QtGui.QVBoxLayout()
        if details:
            splitter = QtGui.QSplitter()
            splitter.setOrientation(Qt.Vertical)
            splitter.addWidget(self._view)
            splitter.addWidget(details)
            layout.addWidget(splitter)
        else:
            layout.addWidget(self._view)
        if label_text:
            layout.addWidget(self._label)
        self.setLayout(layout)

    def view(self):
        """Get the view object (QAbstractItemView)."""
        return self._view

    def label(self):
        """Get the label object (QLabel)."""
        return self._label

    def setData(self, tuple_list, decoration_provider=None, debug=False):
        """Reset the model to have the data shown.

        :param tuple_list: a list of tuples. Each tuple should have
          len(headers) items.
        :param decoration_provider: a callable taking the row number
          and record. It returns the icon to display in the first column
          or None if none.
        """
        # Update the model
        self._model.set_raw_data(tuple_list, decoration_provider)

        # Update the view & label
        row_count = self._model.rowCount()
        self._view.setVisible(row_count > 0)
        if self._type in ['tree', 'table']:
            self._view.resizeColumnToContents(0)
        self._update_label_text(row_count)

    def _update_label_text(self, row_count=0):
        if self._label_text:
            text = self._label_text % {'rows': row_count}
            self._label.setText(text)
    
    def current_row_info(self):
        """Return the index and data tuple for the currently selected row.
        
        :return: index, tuple or None, None is no row is selected
        """
        model_index = self.view().currentIndex()
        model = model_index.model()
        if model is None:
            return None, None
        row = model_index.row()
        return row, self._model.raw_data()[row]

    def data(self):
        """Return the list of tuples stored as data."""
        return self._model.raw_data()


class _TupleListModel(QtGui.QStandardItemModel):

    def __init__(self, tuple_list=None, decoration_provider=None):
        """Initialize the model to have the data shown.

        :param tuple_list: a list of tuples. Each tuple should have
          the same number of items.
        :param decoration_provider: a callable taking the row number
          and record. It returns the icon to display in the first column
          or None if none.
        """
        super(_TupleListModel, self).__init__()
        self.set_raw_data(tuple_list, decoration_provider)
        self._labels = []

    def set_raw_data(self, tuple_list, decoration_provider=None):
        self.clear()
        self._raw_data = tuple_list or []
        self._decoration_provider = decoration_provider
        self._row_count = len(self._raw_data)
        if self._row_count:
            self._column_count = len(tuple_list[0])
            for i in range (0, self._row_count):
                for j in range (0, self._column_count):
                    if self._column_count > 1:
                        item = QtGui.QStandardItem()
                        try:
                            item.setData(QVariant("%s"%tuple_list[i][j]),
                                         Qt.DisplayRole)
                        except (TypeError, ValueError):
                            item.setData(QVariant("Not displayable"),
                                         Qt.DisplayRole)
                        item.setData(QVariant(tuple_list[i][j] or ''))
                    else:
                        item = QtGui.QStandardItem()
                        try:
                            item.setData(QVariant("%s"%tuple_list[i]),
                                         Qt.DisplayRole)
                        except (TypeError, ValueError):
                            item.setData(QVariant("Not displayable"),
                                         Qt.DisplayRole)
                        item.setData(QVariant(tuple_list[i]))
                    if j == 0 and self._decoration_provider:
                        item_icon = self._decoration_provider(i, tuple_list[i])
                        item.setIcon(item_icon)
                    self.setItem(i,j,item)
        else:
            self._column_count = 0

    def raw_data(self):
        return self._raw_data

    def setHorizontalHeaderLabels(self, labels):
        self._labels = labels or []

    def horizontalHeaderLabels(self):
        return self._labels

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role == Qt.TextAlignmentRole:
            if orientation == Qt.Horizontal:
                return QVariant(int(Qt.AlignLeft | Qt.AlignVCenter))
            else:
                return QVariant(int(Qt.AlignRight | Qt.AlignVCenter))
        if role != Qt.DisplayRole:
            return QVariant()
        if orientation == Qt.Horizontal:
            # If labels are defined for columns, use them
            if section < len(self._labels):
                return QVariant(self._labels[section])
        # For rows and unlabelled columns, number them from 1
        return QVariant(int(section) + 1)
