/*
 * Copyright 2009- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation
 * nor does it submit to any jurisdiction.
 */

#include "ComboMulti.hpp"

#include <QAbstractItemView>
#include <QApplication>
#include <QCheckBox>
#include <QDebug>
#include <QItemDelegate>
#include <QPainter>
#include <QStylePainter>

//==========================================================
//
// ComboMulti
//
//==========================================================

ComboMulti::ComboMulti(QWidget* widget) : QComboBox(widget), dpyText_("") {
    setSizeAdjustPolicy(QComboBox::AdjustToContents);

    auto* del = new ComboMultiDelegate(this);

    connect(del, SIGNAL(itemChecked()), this, SLOT(slotChecked()));

    // set delegate items view
    view()->setItemDelegate(del);
    // view()->setStyleSheet("  padding: 15px; ");
    //  Enable editing on items view
    view()->setEditTriggers(QAbstractItemView::CurrentChanged);

    // set "CheckBoxList::eventFilter" as event filter for items view
    view()->viewport()->installEventFilter(this);

    // it just cool to have it as defualt ;)
    view()->setAlternatingRowColors(true);
}

ComboMulti::~ComboMulti() = default;

bool ComboMulti::eventFilter(QObject* object, QEvent* event) {
    // don't close items view after we release the mouse button
    // by simple eating MouseButtonRelease in viewport of items view
    if (event->type() == QEvent::MouseButtonRelease && object == view()->viewport()) {
        return true;
    }
    return QComboBox::eventFilter(object, event);
}

void ComboMulti::paintEvent(QPaintEvent*) {
    QStylePainter painter(this);
    painter.setPen(palette().color(QPalette::Text));

    // draw the combobox frame, focusrect and selected etc.
    QStyleOptionComboBox opt;
    initStyleOption(&opt);

    // if no display text been set , use "..." as default
    if (dpyText_.isEmpty())
        if (mode_ == FilterMode)
            opt.currentText = "ALL";
        else
            opt.currentText = "NONE";
    else {
        opt.currentText = dpyText_;
    }
    painter.drawComplexControl(QStyle::CC_ComboBox, opt);

    // draw the icon and text
    painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
}

void ComboMulti::slotChecked() {
    QString s;
    selection_.clear();

    for (int i = 0; i < model()->rowCount(); i++) {
        if (model()->data(model()->index(i, 0), Qt::CheckStateRole).toBool()) {
            selection_ << model()->data(model()->index(i, 0), Qt::DisplayRole).toString();
        }
    }

    if (selection_.count() == 0)
        s = "";
    else
        s = selection_.join(", ");

    setDisplayText(s);

    update();

    Q_EMIT selectionChanged();
}

void ComboMulti::setSelection(QStringList lst) {
    for (int i = 0; i < count(); i++) {
        setItemData(i, false, Qt::CheckStateRole);
        if (lst.contains(itemText(i)))
            setItemData(i, true, Qt::CheckStateRole);
    }

    slotChecked();
}

void ComboMulti::setSelectionByData(QStringList lst) {
    for (int i = 0; i < count(); i++) {
        setItemData(i, false, Qt::CheckStateRole);
        if (lst.contains(itemData(i).toString()))
            setItemData(i, true, Qt::CheckStateRole);
    }

    slotChecked();
}

void ComboMulti::clearSelection() {
    for (int i = 0; i < count(); i++) {
        setItemData(i, false, Qt::CheckStateRole);
    }

    slotChecked();
}

void ComboMulti::selectSoleItem() {
    if (count() == 1) {
        setItemData(0, true, Qt::CheckStateRole);
        slotChecked();
    }
}

QStringList ComboMulti::selectionData() const {
    QStringList lst;
    for (int i = 0; i < count(); i++) {
        if (itemData(i, Qt::CheckStateRole).toBool())
            lst << itemData(i, Qt::UserRole).toString();
    }
    return lst;
}

void ComboMulti::setDisplayText(QString text) {
    dpyText_ = text;
}

QString ComboMulti::displayText() const {
    return dpyText_;
}

QStringList ComboMulti::all() const {
    QStringList lst;
    for (int i = 0; i < count(); i++)
        lst << itemText(i);

    return lst;
}

void ComboMulti::setMode(Mode mode) {
    mode_ = mode;
}

//==========================================================
//
// ComboMultiDelegate
//
//==========================================================

ComboMultiDelegate::ComboMultiDelegate(QObject* parent) : QItemDelegate(parent) {
}

void ComboMultiDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
    // Get item data
    bool value   = index.data(Qt::CheckStateRole).toBool();
    QString text = index.data(Qt::DisplayRole).toString();

    // fill style options with item data
    const QStyle* style = QApplication::style();
    QStyleOptionButton opt;
    opt.state |= value ? QStyle::State_On : QStyle::State_Off;
    opt.state |= QStyle::State_Enabled;
    opt.text = text;
    opt.rect = option.rect;

    style->drawControl(QStyle::CE_CheckBox, &opt, painter);
}

QWidget* ComboMultiDelegate::createEditor(QWidget* parent,
                                          const QStyleOptionViewItem& /*option*/,
                                          const QModelIndex& /*index*/) const {
    auto* editor = new QCheckBox(parent);
    return editor;
}

void ComboMultiDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const {
    // set editor data
    auto* myEditor = static_cast<QCheckBox*>(editor);
    myEditor->setText(index.data(Qt::DisplayRole).toString());
    myEditor->setChecked(index.data(Qt::CheckStateRole).toBool());

    connect(myEditor, SIGNAL(stateChanged(int)), this, SLOT(slotEdited(int)));
}

void ComboMultiDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const {
    // get the value from the editor (CheckBox)
    auto* myEditor = static_cast<QCheckBox*>(editor);
    bool value     = myEditor->isChecked();

    if (model->data(index, Qt::CheckStateRole).toBool() != value) {
        model->setData(index, value, Qt::CheckStateRole);
        Q_EMIT itemChecked();
    }
}

void ComboMultiDelegate::updateEditorGeometry(QWidget* editor,
                                              const QStyleOptionViewItem& option,
                                              const QModelIndex& /*index*/) const {
    editor->setGeometry(option.rect);
}

void ComboMultiDelegate::slotEdited(int) {
    auto* cb = static_cast<QCheckBox*>(sender());
    if (cb) {
        Q_EMIT commitData(cb);
    }
}
