#include "external_database.h"
#include <QScrollBar>
#include <QFileDialog>
#include <QImageReader>
#include <QDialogButtonBox>
#include <QMessageBox>
#include <QBuffer>
#include "ccm_SHS_shared.h"


ExternalDatabase::ExternalDatabase(QWidget * parent): QDialog(parent), Ui::ExternalDatabase() {
	setupUi(this);
	recordSQL->setReadOnly(true);
	recordSQL->hideColumn("data");
	blockitem.setFlags(QGraphicsItem::GraphicsItemFlags());
	blockitem.hide();
	blockView->addItem(&blockitem);
	blockView->viewport()->installEventFilter(this);
	connect(treeBuses,   SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(treeItemChanged(QTreeWidgetItem*,int)));
	connect(treeVendors, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(treeItemChanged(QTreeWidgetItem*,int)));
	connect(treeGroups,  SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(treeItemChanged(QTreeWidgetItem*,int)));
	msgbox.setWindowTitle("SH Designer");
	msgbox.setIcon(QMessageBox::Question);
	msgbox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
	QGridLayout * ml = (QGridLayout*)msgbox.layout();
	check_for_table = new QCheckBox(tr("For table"));
	check_for_all = new QCheckBox(tr("For all"));
	connect(check_for_all, SIGNAL(toggled(bool)), check_for_table, SLOT(setDisabled(bool)));
	ml->addWidget(check_for_table, ml->rowCount(), 0, 1, ml->columnCount(), Qt::AlignLeft);
	ml->addWidget(check_for_all, ml->rowCount(), 0, 1, ml->columnCount(), Qt::AlignLeft);
}


ExternalDatabase::~ExternalDatabase() {
}


bool ExternalDatabase::showImport(QSqlDatabase & db_) {
	return show(db_, SHCORE->database());
}


bool ExternalDatabase::showExport(QSqlDatabase & db_) {
	SHDatabaseStorage::initDatabase(db_);
	return show(SHCORE->database(), db_);
}


bool ExternalDatabase::show(QSqlDatabase db_from, QSqlDatabase db_to) {
	dbf = db_from;
	dbt = db_to;
	recordSQL->setConnectionName(dbf.connectionName());
	setDatabase();
	checkDependencies();
	return exec() == QDialog::Accepted;
}


bool ExternalDatabase::eventFilter(QObject * o, QEvent * e) {
	if (o == blockView->viewport()) {
		if (e->type() == QEvent::Resize) {
			blockView->centerOn(&blockitem);
		}
	}
	return QWidget::eventFilter(o, e);
}


void ExternalDatabase::setDatabase() {
	fillINDTree(treeBuses, "SELECT id,name FROM bus_types");
	fillINDTree(treeVendors, "SELECT id,name,description FROM vendors");
	fillINDTree(treeGroups, "SELECT id,name,description FROM groups");
	fillINDTree(treeDevices, "SELECT id,name FROM block_types");
}


void ExternalDatabase::fillINDTree(QTreeWidget * tree, const QString & qs) {
	tree->clear();
	tree->setColumnCount(3);
	tree->headerItem()->setText(0, "ID");
	tree->headerItem()->setText(1, "Name");
	tree->headerItem()->setText(2, "Description");
	tree->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
	QSqlQuery q(qs, dbf);
	if (q.lastError().isValid()) return;
	while (q.next()) {
		QSqlRecord r = q.record();
		QTreeWidgetItem * ti = new QTreeWidgetItem();
		ti->setText(0, r.value(0).toString());
		ti->setData(0, Qt::UserRole, r.value(0).toInt());
		ti->setText(1, r.value(1).toString());
		if (r.count() > 2)
			ti->setText(2, r.value(2).toString());
		ti->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable);
		ti->setCheckState(0, Qt::Checked);
		tree->addTopLevelItem(ti);
	}
}


void ExternalDatabase::checkAll(bool on) {
	QTreeWidget * trees[4] = {treeBuses, treeVendors, treeGroups, treeDevices};
	for (int i = 0; i < 4; ++i) {
		trees[i]->blockSignals(true);
		for (int j = 0; j < trees[i]->topLevelItemCount(); ++j)
			trees[i]->topLevelItem(j)->setCheckState(0, on ? Qt::Checked: Qt::Unchecked);
		trees[i]->blockSignals(false);
	}
	checkDependencies();
}


int ExternalDatabase::insertRecord(const QSqlRecord & r, const QString & table, QSqlDatabase & base) {
	QStringList vrs, cols;
	for (int j = 0; j < r.count(); ++j) {
		vrs << QString(":%1").arg(j);
		cols << r.fieldName(j);
	}
	QSqlQuery iq(base);
	iq.prepare(QString("INSERT INTO %1 (%2) VALUES (%3)").arg(table).arg(cols.join(",")).arg(vrs.join(",")));
	//qDebug() << iq.lastQuery();
	for (int j = 0; j < r.count(); ++j)
		iq.bindValue(QString(":%1").arg(j), r.value(j));
	//qDebug() << iq.lastQuery();
	if (!iq.exec())
		qDebug() << iq.lastError() << iq.lastQuery();
	return iq.lastInsertId().toInt();
}


void ExternalDatabase::removeRecord(int id, const QString & table, QSqlDatabase & base) {
	QSqlQuery iq(base);
	iq.prepare(QString("DELETE FROM %1 WHERE id='%2'").arg(table).arg(id));
	if (!iq.exec())
		qDebug() << iq.lastError() << iq.lastQuery();
}

/*
int ExternalDatabase::lastInsertedID(QSqlDatabase & base) {
	QSqlQuery q(base);
	q.exec("select last_insert_rowid()");
	q.next();
	return q.value(0).toInt();

}
*/

void ExternalDatabase::ok() {
	QApplication::setOverrideCursor(Qt::WaitCursor);
	QTreeWidget * trees[4] = {treeBuses, treeVendors, treeGroups, treeDevices};
	QString tnames[4] = {"bus_types", "vendors", "groups", "block_types"};
	QString tcols[4] = {"id,name,data", "*", "*", "id,name,data"};
	QString msg_type[4] = {"Bus", "Vendor", "Group", "Device"};
	QStringList sel_ids[4];
	for (int i = 0; i < 4; ++i) {
		for (int j = 0; j < trees[i]->topLevelItemCount(); ++j) {
			QTreeWidgetItem * ti = trees[i]->topLevelItem(j);
			if (ti->checkState(0) == Qt::Checked)
				sel_ids[i] << ti->text(0);
		}
	}
	dbt.transaction();
	QMap<int, int> map_id[3]; // what id use instead of existings
	QMap<QString, int> existing[4]; // existings names with id
	for (int i = 0; i < 4; ++i) {
		QSqlQuery eq(QString("SELECT id,name FROM %1").arg(tnames[i]), dbt);
		while (eq.next()) {
			QSqlRecord r = eq.record();
			existing[i][r.value("name").toString().trimmed().toLower()] = r.value("id").toInt();
		}
	}
	msgbox.button(QMessageBox::Yes)->setText(tr("Replace"));
	msgbox.button(QMessageBox::No)->setText(tr("Use existing"));
	int mret = -1, max_id = -1;
	check_for_all->setChecked(false);
	BusType bus;
	BlockType block;
	BlockItem blockitem;
	for (int i = 0; i < 4; ++i) {
		check_for_table->setChecked(false);
		QSqlQuery mq(QString("SELECT max(id) FROM %1").arg(tnames[i]), dbt);
		mq.next();
		max_id = mq.value(0).toInt();
		QSqlQuery q(QString("SELECT %1 FROM %2 WHERE id IN (%3)").arg(tcols[i], tnames[i]).arg(sel_ids[i].join(",")), dbf);
		while (q.next()) {
			QSqlRecord r = q.record();
			QString rname = r.value("name").toString().trimmed().toLower();
			int pid = r.value("id").toInt();
			bool ex = existing[i].contains(rname);
			if (ex) {
				if (!check_for_all->isChecked() && !check_for_table->isChecked()) {
					msgbox.setText(QString("%1 \"%2\" already exists in database. Select action.").arg(msg_type[i], r.value("name").toString()));
					QApplication::setOverrideCursor(Qt::ArrowCursor);
					mret = msgbox.exec();
					QApplication::restoreOverrideCursor();
				}
				if (i < 3) {
					map_id[i][pid] = existing[i].value(rname);
					pid = map_id[i][pid];
				}
				if (mret == QMessageBox::Yes) {
					removeRecord(pid, tnames[i], dbt);
				}
			} else {
				++max_id;
				if (i < 3)
					map_id[i][pid] = max_id;
				pid = max_id;
			}
			if (!ex || (mret == QMessageBox::Yes)) {
				r.setValue("id", pid);
				if (i == 0) { // bus_type
					BusType_load(bus, r);
					r.setValue("data", piqSerialize(bus));
				}
				if (i == 3) { // block_type
					BlockType_load(block, r);
					block.vendor_id = map_id[1].value(block.vendor_id, block.vendor_id);
					block.group_id  = map_id[2].value(block.group_id , block.group_id );
					blockitem.loadModel(block.model);
					QVector<BlockItemPin * > pins = blockitem.pins();
					foreach (BlockItemPin * p, pins)
						p->setBusType(map_id[0].value(p->busType(), p->busType()));
					block.model = blockitem.saveModel();
					r.setValue("data", piqSerialize(block));
				}
				insertRecord(r, tnames[i], dbt);
			}
		}
	}
	dbt.commit();
	QApplication::restoreOverrideCursor();
	QDialog::accept();
}


void ExternalDatabase::checkDependencies() {
	QSet<int> bids, vids, gids;
	BlockType block;
	BlockItem blockitem;
	QStringList dids;
	for (int i = 0; i < treeDevices->topLevelItemCount(); ++i) {
		QTreeWidgetItem * ti = treeDevices->topLevelItem(i);
		if (ti->checkState(0) == Qt::Unchecked) continue;
		dids << ti->text(0);
	}
	QSqlQuery q(QString("SELECT * FROM block_types WHERE id IN (%1)").arg(dids.join(",")), dbf);
	if (q.lastError().isValid()) return;
	while (q.next()) {
		QSqlRecord r = q.record();
		BlockType_load(block, r);
		blockitem.loadModel(block.model);
		QVector<BlockItemPin * > pins = blockitem.pins();
		foreach (BlockItemPin * p, pins)
			bids << p->busType();
		vids << block.vendor_id;
		gids << block.group_id;
	}
	QSet<int> * ids[3] = {&bids, &vids, &gids};
	QTreeWidget * trees[3] = {treeBuses, treeVendors, treeGroups};
	for (int i = 0; i < 3; ++i) {
		for (int j = 0; j < trees[i]->topLevelItemCount(); ++j) {
			QTreeWidgetItem * ti = trees[i]->topLevelItem(j);
			if (ids[i]->contains(ti->text(0).toInt())) {
				ti->setDisabled(true);
				ti->setCheckState(0, Qt::Checked);
			} else
				ti->setDisabled(false);
		}
	}
	//qDebug() << bids << vids << gids;
}


void ExternalDatabase::on_treeDevices_currentItemChanged(QTreeWidgetItem * item, QTreeWidgetItem * ) {
	if (!item) {
		blockitem.hide();
		return;
	}
	QSqlQuery q(QString("SELECT * FROM block_types WHERE id=%1").arg(item->data(0, Qt::UserRole).toInt()), dbf);
	QSqlRecord r;
	if (q.next()) {
		blockitem.show();
		r = q.record();
	} else {
		blockitem.hide();
		return;
	}
	recordSQL->updateRelations();
	recordSQL->setRecord(r);
	BlockType block;
	BlockType_load(block, r);
	blockitem.loadModel(block.model);
	blockitem.show();
}


void ExternalDatabase::on_treeDevices_itemChanged(QTreeWidgetItem * item, int) {
	Qt::CheckState cs = item->checkState(0) == Qt::Checked ? Qt::Checked : Qt::Unchecked;
	QList<QTreeWidgetItem * > sil = treeDevices->selectedItems();
	QMetaObject::invokeMethod(this, "checkDependencies", Qt::QueuedConnection);
	if (!sil.contains(item)) return;
	treeDevices->blockSignals(true);
	foreach (QTreeWidgetItem * i, sil)
		if (item != i)
			i->setCheckState(0, cs);
	treeDevices->blockSignals(false);
}


void ExternalDatabase::treeItemChanged(QTreeWidgetItem * item, int) {
	QTreeWidget * tree = qobject_cast<QTreeWidget * >(sender());
	if (!tree || !item) return;
	Qt::CheckState cs = item->checkState(0) == Qt::Checked ? Qt::Checked : Qt::Unchecked;
	QList<QTreeWidgetItem * > sil = tree->selectedItems();
	if (!sil.contains(item)) return;
	tree->blockSignals(true);
	foreach (QTreeWidgetItem * i, sil)
		if (item != i && !i->isDisabled())
			i->setCheckState(0, cs);
	tree->blockSignals(false);
}
