#include "ecombobox.h"

#include "clineedit.h"
#include "qabstractitemview.h"
#include "qad_types.h"
#include "qwidget.h"

#include <QApplication>
#include <QHeaderView>
#include <QLabel>
#include <QLayout>
#include <QStandardItemModel>
#include <QTreeView>


class EModel: public QStandardItemModel {
public:
	EModel(QObject * parent = nullptr): QStandardItemModel(parent) {
#if QT_VERSION < 0x050000
		setSupportedDragActions(Qt::MoveAction);
#endif
	}

protected:
	virtual Qt::ItemFlags flags(const QModelIndex & index) const override {
		Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
		if (!index.isValid()) f |= Qt::ItemIsDropEnabled;
		return f;
	}
#if QT_VERSION >= 0x050000
	Qt::DropActions supportedDragActions() const override { return Qt::MoveAction; }
	Qt::DropActions supportedDropActions() const override { return Qt::MoveAction; }
#endif
};


EComboBox::EComboBox(QWidget * parent)
	: QComboBox(parent)
	, iv(new QTreeView(this))
	, header(new QWidget(this))
	, icon(new QLabel(this))
	, filter(new CLineEdit(this)) {
	setView(iv);
	setModel(new EModel());
	iv->setTextElideMode(Qt::ElideMiddle);
	iv->setRootIsDecorated(false);
	iv->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
	iv->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
	iv->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
	iv->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
	iv->setMinimumHeight(100);
	icon->setPixmap(QPixmap(":/icons/edit-find.png"));
	icon->setScaledContents(true);
	icon->setFixedSize(preferredIconSize(1.2, this));
	ifont = nfont = font();
	ifont.setItalic(true);
#if QT_VERSION >= 0x040700
	filter->setPlaceholderText(tr("Filter"));
	filter->setFont(ifont);
#endif
	header->setAutoFillBackground(true);
	header->setLayout(new QBoxLayout(QBoxLayout::LeftToRight));
	header->layout()->setSpacing(2);
	header->layout()->setContentsMargins(2, 0, 0, 0);
	header->layout()->addWidget(icon);
	header->layout()->addWidget(filter);
	header->setParent(iv->header());
	connect(filter, &CLineEdit::textChanged, this, [this](const QString & text) { EComboBox::filterChanged(text, false); });
	connect(model(), &EModel::layoutChanged, this, &EComboBox::rowsChanged);
	connect(filter, &QLineEdit::returnPressed, this, [this]() {
		if (rows_visible != 1) return;
		setCurrentIndex(first_index.row());
	});
}


QSize EComboBox::sizeHint() const {
	QSize s = QComboBox::sizeHint();
	s.setWidth(s.width() + 16);
	return s;
}


void EComboBox::showPopup() {
	iv->setDragDropMode(isEditable() ? QAbstractItemView::InternalMove : QAbstractItemView::NoDragDrop);
	filterChanged(filter->text(), true);
	QComboBox::showPopup();
	QRect r = iv->header()->rect();
	header->setGeometry(r.x(), r.y(), r.width(), r.height() - 1);
	filter->setFocus();
	filter->selectAll();
}


void EComboBox::changeEvent(QEvent * e) {
	QComboBox::changeEvent(e);
	switch (e->type()) {
	case QEvent::LanguageChange:
#if QT_VERSION >= 0x040700
		filter->setPlaceholderText(tr("Filter"));
#endif
		break;
	default: break;
	}
}


void EComboBox::filterChanged(const QString & text, bool first) {
	if (filter->text().isEmpty()) {
		filter->setFont(ifont);
	} else {
		filter->setFont(nfont);
	}
	iv->hide();
	QModelIndex pi = iv->rootIndex();
	int row_count  = iv->model()->rowCount();
	rows_visible   = 0;
	if (text.isEmpty()) {
		for (int i = 0; i < row_count; ++i) {
			iv->setRowHidden(i, pi, false);
			iv->model()->setData(iv->model()->index(i, 0), iv->model()->index(i, 0, pi).data().toString(), Qt::ToolTipRole);
		}
		iv->show();
		if (first) return;
		hidePopup();
		showPopup();
		qApp->processEvents();
		QRect r = iv->header()->rect();
		header->setGeometry(r.x(), r.y(), r.width(), r.height() - 1);
		return;
	}
	for (int i = 0; i < row_count; ++i) {
		bool hidden = !iv->model()->index(i, 0, pi).data().toString().contains(
#if QT_VERSION_MAJOR <= 5
			QRegExp(text, Qt::CaseInsensitive)
#else
			QRegularExpression(text, QRegularExpression::CaseInsensitiveOption)
#endif
		);
		if (!hidden) {
			if (rows_visible == 0) first_index = iv->model()->index(i, 0);
			++rows_visible;
		}
		iv->setRowHidden(i, pi, hidden);
		iv->model()->setData(iv->model()->index(i, 0), iv->model()->index(i, 0, pi).data().toString(), Qt::ToolTipRole);
	}
	iv->show();
	qApp->processEvents();
	QRect r = iv->header()->rect();
	header->setGeometry(r.x(), r.y(), r.width(), r.height() - 1);
}
