#include "model_delegates.h"
#include <QScrollBar>
#include <QComboBox>
#include <QListWidget>
#include <QFileDialog>
#include <QImageReader>
#include <QHeaderView>
#include <QToolButton>
#include <QSqlField>
#include <QToolBar>
#include <QLineEdit>
#include <QBoxLayout>
#include <QCheckBox>
#include <QApplication>
#include <QLocale>
#include <QBuffer>
#include <QTimer>
#include <QSqlQuery>
#include <clineedit.h>
#include <qvariantedit.h>
#include "core.h"


// QMap<QString, BlockItemPin::Device> variant2devices(const QVariant & v) {
// 	QByteArray ba = v.toByteArray();
// 	QMap<QString, BlockItemPin::Device> devices;
// 	QDataStream bas(ba); bas >> devices;
// 	return devices;
// }
// 
// 
// QVariant devices2variant(const QMap<QString, BlockItemPin::Device> & v) {
// 	QByteArray ba;
// 	QDataStream bas(&ba, QIODevice::ReadWrite); bas << v;
// 	return QVariant(ba);
// }


/// ***********************************************************
/// ***********************************************************
/// ***********************************************************
/// ***********************************************************

/*
TreePathes::TreePathes(QWidget * parent): QTreeWidget(parent) {
	max_devices = 0;
	setColumnCount(5);
	setHeaderLabels(QStringList() << tr("Name") << tr("Editable") << tr("Path"));
	setSortingEnabled(false);
	setSelectionMode(QAbstractItemView::QAbstractItemView::ExtendedSelection);
	setRootIsDecorated(false);
	header()->setMovable(false);
	header()->setResizeMode(1, QHeaderView::ResizeToContents);
	header()->setResizeMode(3, QHeaderView::Fixed);
	header()->setResizeMode(4, QHeaderView::Fixed);
	header()->setStretchLastSection(false);
	setColumnWidth(3, 30);
	setColumnWidth(4, 30);
	//header()->setFixedHeight(30);
	buttonAdd = new QToolButton(header()); buttonAdd->setIcon(QIcon(":/icons/list-add-.png")); buttonAdd->setCursor(Qt::ArrowCursor);
	buttonDel = new QToolButton(header()); buttonDel->setIcon(QIcon(":/icons/list-remove-.png")); buttonDel->setCursor(Qt::ArrowCursor);
	connect(header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(adjustButtons()));
	connect(header(), SIGNAL(geometriesChanged()), this, SLOT(adjustButtons()));
	connect(buttonAdd, SIGNAL(clicked(bool)), this, SLOT(addDevice()));
	connect(buttonDel, SIGNAL(clicked(bool)), this, SLOT(delDevice()));
}


void TreePathes::setDevices(const QMap<QString, BlockItemPin::Device> & devs) {
	QMapIterator<QString, BlockItemPin::Device> it(devs);
	while (it.hasNext()) {
		it.next();
		QTreeWidgetItem * ni = new QTreeWidgetItem();
		ni->setFlags(ni->flags() | Qt::ItemIsEditable);
		ni->setText(0, it.value().name);
		ni->setCheckState(1, it.value().editable ? Qt::Checked : Qt::Unchecked);
		ni->setText(2, it.value().devFPath);
		addTopLevelItem(ni);
	}
}


QMap<QString, BlockItemPin::Device> TreePathes::devices() const {
	QMap<QString, BlockItemPin::Device> ret;
	for (int i = 0; i < topLevelItemCount(); ++i) {
		BlockItemPin::Device d;
		d.name = topLevelItem(i)->text(0);
		d.editable = (topLevelItem(i)->checkState(1) == Qt::Checked);
		d.devFPath = topLevelItem(i)->text(2);
		ret[d.name] = d;
	}
	return ret;
}


void TreePathes::adjustButtons() {
	QRect r(header()->sectionPosition(3), 0, header()->sectionSize(3), header()->height());
	buttonAdd->setGeometry(r);
	r = QRect(header()->sectionPosition(4), 0, header()->sectionSize(4), header()->height());
	buttonDel->setGeometry(r);
}


void TreePathes::addDevice() {
	//if (topLevelItemCount() >= max_devices) return;
	QTreeWidgetItem * ni = new QTreeWidgetItem();
		ni->setFlags(ni->flags() | Qt::ItemIsEditable);
	if (!classes.isEmpty())
		ni->setText(2, SHCORE->io_prefixes.value(classes[0]));
	ni->setCheckState(1, Qt::Checked);
	addTopLevelItem(ni);
}


void TreePathes::delDevice() {
	qDeleteAll(selectedItems());
}
*/

/// ***********************************************************
/// ***********************************************************
/// ***********************************************************
/// ***********************************************************


QWidget * PinAlignDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {
	QComboBox * combo = new QComboBox(parent);
	int cv = index.data().toInt();
	combo->addItem("Left", int(Qt::AlignLeft)); if (cv == Qt::AlignLeft) combo->setCurrentIndex(0);
	combo->addItem("Right", int(Qt::AlignRight)); if (cv == Qt::AlignRight) combo->setCurrentIndex(1);
	combo->addItem("Top", int(Qt::AlignTop)); if (cv == Qt::AlignTop) combo->setCurrentIndex(2);
	combo->addItem("Bottom", int(Qt::AlignBottom)); if (cv == Qt::AlignBottom) combo->setCurrentIndex(3);
	combo->setGeometry(option.rect);
	return combo;
}


QString PinAlignDelegate::displayText(const QVariant & value, const QLocale & locale) const {
	int cv = value.toInt();
	switch (cv) {
	case Qt::AlignLeft: return "Left"; break;
	case Qt::AlignRight: return "Right"; break;
	case Qt::AlignTop: return "Top"; break;
	case Qt::AlignBottom: return "Bottom"; break;
	}
	return "unknown";
}


void PinAlignDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
	model->setData(index, ((QComboBox*)editor)->itemData(((QComboBox*)editor)->currentIndex()).toInt());
}


/// ***********************************************************
/// ***********************************************************
/// ***********************************************************
/// ***********************************************************


QWidget * PinBusDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {
	QComboBox * combo = new QComboBox(parent);
	int cv = index.data().toInt();
	foreach (const ISPair & b, buses) {
		combo->addItem(b.second, b.first);
		if (cv == b.first)
			combo->setCurrentIndex(combo->count() - 1);
	}
	return combo;
}


QString PinBusDelegate::displayText(const QVariant & value, const QLocale & locale) const {
	int cv = value.toInt();
	foreach (const ISPair & b, buses)
		if (cv == b.first)
			return b.second;
	return "unknown";
}


void PinBusDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
	model->setData(index, ((QComboBox*)editor)->itemData(((QComboBox*)editor)->currentIndex()).toInt());
}


void PinBusDelegate::updateDB() {
	buses.clear();
	if (!SHDesignerCore::isDefaultDBOpened())
		return;
	QSqlQuery q(QString("SELECT id,name FROM bus_types"));
	while (q.next()) {
		QSqlRecord r = q.record();
		buses << ISPair(r.field("id").value().toInt(), r.field("name").value().toString());
	}
}


/// ***********************************************************
/// ***********************************************************
/// ***********************************************************
/// ***********************************************************

/*
QWidget * PinDeviceDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {
	if (!index.model()) return 0;
	QMap<QString, BlockItemPin::Device> dm = variant2devices(index.data());
	int bus_id = index.model()->index(index.row(), index.column() - 1).data().toInt();
	//QSqlQuery q(QString("SELECT devices,io_classes FROM bus_types WHERE id=%1").arg(bus_id));
	QSqlQuery q(QString("SELECT io_classes FROM bus_types WHERE id=%1").arg(bus_id));
	int devices = 1;
	QStringList classes;
	if (q.next()) {
		//devices = q.value(0).toInt();
		classes = q.value(0).toString().split(";");
	}
	//if (devices <= 0) return 0;
	//if (devices == 1) {
		QWidget * w = new QWidget(parent);
		QBoxLayout * lay = new QBoxLayout(QBoxLayout::LeftToRight);
		lay->setContentsMargins(0, 0, 0, 0);
		CLineEdit * ed = new CLineEdit();
		QCheckBox * cb = new QCheckBox(tr("Editable"));
		//w->setFocusPolicy(Qt::StrongFocus);
		//ed->setFocusProxy(w);
		lay->addWidget(ed);
		lay->addWidget(cb);
		//qDebug() << dm.size();
		if (!dm.isEmpty()) {
			//qDebug() << dm.values().front().devFPath;
			if (dm.values().front().devFPath.isEmpty() && !classes.isEmpty())
				ed->setText(SHCORE->io_prefixes.value(classes[0]));
			else
				ed->setText(dm.values().front().devFPath);
			cb->setChecked(dm.values().front().editable);
		} else {
			if (!classes.isEmpty())
				ed->setText(SHCORE->io_prefixes.value(classes[0]));
		}
		w->setLayout(lay);
		QTimer::singleShot(0, ed, SLOT(setFocus()));
		return w;
	//}
	TreePathes * list = new TreePathes(parent);
	list->max_devices = devices;
	list->classes = classes;
	list->setDevices(dm);
	list->setGeometry(option.rect);
	list->setMinimumHeight(150);
	QRect lg = list->geometry(), tg = parent->rect();
	const_cast<QStyleOptionViewItem &>(option).rect.setHeight(list->minimumHeight());
	if (lg.bottom() > tg.height())
		const_cast<QStyleOptionViewItem &>(option).rect.moveBottom(tg.height());
	return list;
}


QString PinDeviceDelegate::displayText(const QVariant & value, const QLocale & locale) const {
	return QString();
}


void PinDeviceDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
	if (!editor) return;
	QMap<QString, BlockItemPin::Device> dm;
	if (qobject_cast<TreePathes*>(editor))
		dm = ((TreePathes*)editor)->devices();
	else {
		if (!editor->layout()) return;
		CLineEdit * ed = qobject_cast<CLineEdit * >(editor->layout()->itemAt(0)->widget());
		QCheckBox * cb = qobject_cast<QCheckBox * >(editor->layout()->itemAt(1)->widget());
		if (!cb || !ed) return;
		dm[""] = BlockItemPin::Device("", ed->text(), cb->isChecked());
	}
	model->setData(index, devices2variant(dm));
}


QString PinDeviceDelegate::getText(const QVariant & value) const {
	QMap<QString, BlockItemPin::Device> dm(variant2devices(value));
	QMapIterator<QString, BlockItemPin::Device> it(dm);
	QString ret;
	while (it.hasNext()) {
		it.next();
		if (!ret.isEmpty()) ret += "\n";
		if (!it.key().isEmpty()) ret += it.key() + ": ";
		ret += QString("%1 (%2)").arg(it.value().devFPath, (it.value().editable ? "e" : "ne"));
	}
	int cv = value.toInt();
	return ret;
}


void PinDeviceDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
	QString s = getText(index.data());
	QStyledItemDelegate::paint(painter, option, index);
	painter->setPen(option.palette.color(option.state.testFlag(QStyle::State_Selected) ? QPalette::HighlightedText : QPalette::WindowText));
	painter->drawText(option.rect, Qt::AlignLeft | Qt::AlignVCenter, s);
}


QSize PinDeviceDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const {
	QString s = getText(index.data());
	//qDebug() << option.fontMetrics.height(s).size();
	return option.fontMetrics.size(Qt::AlignLeft | Qt::AlignVCenter, s);
}
*/

/// ***********************************************************
/// ***********************************************************
/// ***********************************************************
/// ***********************************************************


FlagsDelegate::FlagsDelegate(QObject * parent): QStyledItemDelegate(parent) {
	single = false;
}


QWidget * FlagsDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {
	if (single) {
		QComboBox * combo = new QComboBox(parent);
		int val = index.data().toInt();
		for (int i = 0; i < me.keyCount(); ++i) {
			if (hidden_.contains(QString(me.key(i)))) continue;
			combo->addItem(me.key(i), me.value(i));
			if (me.value(i) == val)
				combo->setCurrentIndex(combo->count() - 1);
		}
		combo->setGeometry(option.rect);
		return combo;
	} else {
		QListWidget * list = new QListWidget(parent);
		int val = index.data().toInt();
		for (int i = 0; i < me.keyCount(); ++i) {
			if (hidden_.contains(QString(me.key(i)))) continue;
			QListWidgetItem * li = new QListWidgetItem(me.key(i));
			li->setCheckState((me.value(i) & val) == me.value(i) ? Qt::Checked : Qt::Unchecked);
			li->setData(Qt::UserRole, me.value(i));
			list->addItem(li);
		}
		list->setGeometry(option.rect);
		list->setMinimumHeight(list->sizeHintForIndex(list->model()->index(0, 0)).height() * (list->count() + 1));
		QRect lg = list->geometry(), tg = parent->rect();
		const_cast<QStyleOptionViewItem &>(option).rect.setHeight(list->minimumHeight());
		if (lg.bottom() > tg.height())
			const_cast<QStyleOptionViewItem &>(option).rect.moveBottom(tg.height());
		return list;
	}
}


void FlagsDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
	int val = 0;
	if (single) {
		QComboBox * combo = qobject_cast<QComboBox * >(editor);
		if (combo == 0) return;
		val = combo->currentData().toInt();
	} else {
		QListWidget * list = qobject_cast<QListWidget * >(editor);
		if (list == 0) return;
		for (int i = 0; i < list->count(); ++i) {
			QListWidgetItem * li = list->item(i);
			if (li->checkState() == Qt::Checked)
				val |= li->data(Qt::UserRole).toInt();
		}
	}
	model->setData(index, val);
}


QString FlagsDelegate::displayText(const QVariant & value, const QLocale & locale) const {
	QString ret;
	int val = value.toInt();
	for (int i = 0; i < me.keyCount(); ++i) {
		if ((me.value(i) & val) != me.value(i)) continue;
		if (!ret.isEmpty()) ret += " | ";
		ret += me.key(i);
	}
	return ret;
}


/// ***********************************************************
/// ***********************************************************
/// ***********************************************************
/// ***********************************************************


PacketPinDelegate::PacketPinDelegate(QObject * parent): QStyledItemDelegate(parent) {
	pins = 0;
}


QWidget * PacketPinDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {
	QComboBox * combo = new QComboBox(parent);
	if (!pins)
		return combo;
	QString val = index.data().toString();
	for (int i = 0; i < pins->topLevelItemCount(); ++i) {
		QTreeWidgetItem * ti = pins->topLevelItem(i);
		for (int j = 0; j < ti->childCount(); ++j) {
			QString ni = ti->child(j)->text(0);
			combo->addItem(ni);
			if (ni == val)
				combo->setCurrentIndex(i);
		}
	}
	return combo;
}


void PacketPinDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
	QComboBox * combo = qobject_cast<QComboBox * >(editor);
	if (combo == 0) return;
	model->setData(index, combo->currentText());
}


/// ***********************************************************
/// ***********************************************************
/// ***********************************************************
/// ***********************************************************


QWidget * PinPluginDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {
	QComboBox * combo = new QComboBox(parent);
	QString val = index.data().toString();
	auto pil = RUNTIME->plugins_manager->localInterface().getPlugins(SHPluginBase::Packet);
	combo->addItem("");
	for (const auto & i: pil) {
		QString pn = i.plugin->name();
		combo->addItem(pn);
		if (pn == val)
			combo->setCurrentIndex(combo->count() - 1);
	}
	return combo;
}


void PinPluginDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
	QComboBox * combo = qobject_cast<QComboBox * >(editor);
	if (combo == 0) return;
	model->setData(index, combo->currentText());
}


/// ***********************************************************
/// ***********************************************************
/// ***********************************************************
/// ***********************************************************


PacketReplyByDelegate::PacketReplyByDelegate(QObject * parent): QStyledItemDelegate(parent) {
	packets = 0;
}


QWidget * PacketReplyByDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {
	QComboBox * combo = new QComboBox(parent);
	combo->addItem("");
	if (!packets)
		return combo;
	QString val = index.data().toString();
	for (int i = 0; i < packets->topLevelItemCount(); ++i) {
		QTreeWidgetItem * ti = packets->topLevelItem(i);
		QString ni = ti->text(0);
		combo->addItem(ni);
		if (ni == val)
			combo->setCurrentIndex(i + 1);
	}
	return combo;
}


void PacketReplyByDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
	QComboBox * combo = qobject_cast<QComboBox * >(editor);
	if (combo == 0) return;
	model->setData(index, combo->currentText());
}


/// ***********************************************************
/// ***********************************************************
/// ***********************************************************
/// ***********************************************************


VariantTypeDelegate::VariantTypeDelegate(QObject * parent): QStyledItemDelegate(parent) {
}


QWidget * VariantTypeDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {
	static const int types[] = {QVariant::Bool, QVariant::Int, QVariant::Double, QVariant::String,
								QVariant::StringList, QVariant::Color,
								qMetaTypeId<QAD::Enum>(),
								qMetaTypeId<QAD::File>(),
								qMetaTypeId<QAD::Dir>(), -1};
	QComboBox * combo = new QComboBox(parent);
	int val = index.data().toInt();
	int i = -1;
	QLocale loc;
	while (types[++i] >= 0) {
		//qDebug() << types[i];
		combo->addItem(displayText(types[i], loc), types[i]);
		if (val == types[i])
			combo->setCurrentIndex(i);
	}
	return combo;
}


QString VariantTypeDelegate::displayText(const QVariant & value, const QLocale &) const {
	int t = value.toInt();
	if (t == QVariant::String) return tr("String");
	if (t == qMetaTypeId<QAD::Enum>()) return tr("Enum");
	if (t == qMetaTypeId<QAD::File>()) return tr("File");
	if (t == qMetaTypeId<QAD::Dir>()) return tr("Directory");
	return QVariant::typeToName((QVariant::Type)t);
}


void VariantTypeDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
	QComboBox * combo = qobject_cast<QComboBox * >(editor);
	if (combo == 0) return;
	model->setData(index, combo->itemData(combo->currentIndex()).toInt());
}
