/*
 *  Copyright (C) 1999-2002 Bernd Gehrmann
 *                          bernd@mail.berlios.de
 *
 * 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "loglist.h"

#include <qapplication.h>
#include <qnamespace.h>

#include <QHeaderView>

#include <KConfig>
#include <KConfigGroup>
#include <klocalizedstring.h>

#include "loginfo.h"
#include "misc.h"
#include "tooltip.h"

class LogListViewItem : public QTreeWidgetItem
{
public:
    enum { Revision, Author, Date, Branch, Comment, Tags };

    LogListViewItem(QTreeWidget *list, const Cervisia::LogInfo &logInfo);

    bool operator<(const QTreeWidgetItem &other) const override;

private:
    static QString truncateLine(const QString &s);

    Cervisia::LogInfo m_logInfo;
    friend class LogListView;
};

LogListViewItem::LogListViewItem(QTreeWidget *list, const Cervisia::LogInfo &logInfo)
    : QTreeWidgetItem(list)
    , m_logInfo(logInfo)
{
    setText(Revision, logInfo.m_revision);
    setText(Author, logInfo.m_author);
    setText(Date, logInfo.dateTimeToString());
    setText(Comment, truncateLine(logInfo.m_comment));

    for (Cervisia::LogInfo::TTagInfoSeq::const_iterator it = logInfo.m_tags.begin(); it != logInfo.m_tags.end(); ++it) {
        const Cervisia::TagInfo &tagInfo(*it);

        if (tagInfo.m_type == Cervisia::TagInfo::OnBranch) {
            setText(Branch, tagInfo.m_name);
        }
    }

    setText(Tags, logInfo.tagsToString(Cervisia::TagInfo::Tag, Cervisia::LogInfo::NoTagType, QLatin1String(", ")));
}

QString LogListViewItem::truncateLine(const QString &s)
{
    int pos;

    QString res = s.simplified();
    if ((pos = res.indexOf('\n')) != -1)
        res = res.left(pos) + "...";

    return res;
}

bool LogListViewItem::operator<(const QTreeWidgetItem &other) const
{
    const auto &item = static_cast<const LogListViewItem &>(other);

    switch (treeWidget()->sortColumn()) {
    case Revision:
        return ::compareRevisions(m_logInfo.m_revision, item.m_logInfo.m_revision) == -1;
    case Date:
        return ::compare(m_logInfo.m_dateTime, item.m_logInfo.m_dateTime) == -1;
    }

    return QTreeWidgetItem::operator<(other);
}

LogListView::LogListView(KConfig &cfg, QWidget *parent)
    : QTreeWidget(parent)
    , partConfig(cfg)
{
    setAllColumnsShowFocus(true);
    header()->setSortIndicatorShown(true);
    setSelectionMode(QAbstractItemView::NoSelection);
    setRootIsDecorated(false);
    setSortingEnabled(true);
    sortByColumn(LogListViewItem::Revision, Qt::DescendingOrder);
    setHeaderLabels(QStringList() << i18n("Revision") << i18n("Author") << i18n("Date") << i18n("Branch") << i18n("Comment") << i18n("Tags"));

    auto toolTip = new Cervisia::ToolTip(viewport());

    connect(toolTip, SIGNAL(queryToolTip(QPoint, QRect &, QString &)), this, SLOT(slotQueryToolTip(QPoint, QRect &, QString &)));

    QByteArray state = cfg.group("LogList view").readEntry<QByteArray>("Columns", QByteArray());
    header()->restoreState(state);
}

LogListView::~LogListView()
{
    partConfig.group("LogList view").writeEntry("Columns", header()->saveState());
}

void LogListView::addRevision(const Cervisia::LogInfo &logInfo)
{
    (void)new LogListViewItem(this, logInfo);
}

void LogListView::setSelectedPair(const QString &selectionA, const QString &selectionB)
{
    for (int j = 0; j < topLevelItemCount(); j++) {
        auto i = static_cast<LogListViewItem *>(topLevelItem(j));
        i->setSelected(selectionA == i->text(LogListViewItem::Revision) || selectionB == i->text(LogListViewItem::Revision));
    }
}

void LogListView::mousePressEvent(QMouseEvent *e)
{
    // Retrieve selected item
    const LogListViewItem *selItem = static_cast<LogListViewItem *>(itemAt(e->pos()));
    if (!selItem)
        return;

    // Retrieve revision
    const QString revision = selItem->text(LogListViewItem::Revision);

    if (e->button() == Qt::LeftButton) {
        // If the control key was pressed, then we change revision B not A
        if (e->modifiers() & Qt::ControlModifier)
            Q_EMIT revisionClicked(revision, true);
        else
            Q_EMIT revisionClicked(revision, false);
    } else if (e->button() == Qt::MiddleButton)
        Q_EMIT revisionClicked(revision, true);
}

void LogListView::keyPressEvent(QKeyEvent *e)
{
    switch (e->key()) {
    case Qt::Key_A:
        if (currentItem())
            Q_EMIT revisionClicked(currentItem()->text(LogListViewItem::Revision), false);
        break;
        break;
    case Qt::Key_B:
        if (currentItem())
            Q_EMIT revisionClicked(currentItem()->text(LogListViewItem::Revision), true);
        break;
    case Qt::Key_Backspace:
    case Qt::Key_Delete:
    case Qt::Key_Down:
    case Qt::Key_Up:
    case Qt::Key_Home:
    case Qt::Key_End:
    case Qt::Key_PageDown:
    case Qt::Key_PageUp:
        if (e->modifiers() == Qt::NoModifier)
            QTreeWidget::keyPressEvent(e);
        else
            QApplication::postEvent(this, new QKeyEvent(QEvent::KeyPress, e->key(), Qt::NoModifier, e->text()));
        break;
    default:
        // Ignore Key_Enter, Key_Return
        e->ignore();
    }
}

void LogListView::slotQueryToolTip(const QPoint &viewportPos, QRect &viewportRect, QString &text)
{
    if (const LogListViewItem *item = static_cast<LogListViewItem *>(itemAt(viewportPos))) {
        viewportRect = visualRect(indexAt(viewportPos));
        text = item->m_logInfo.createToolTipText();
    }
}

// Local Variables:
// c-basic-offset: 4
// End:
