#include "sql_record_widget.h"

#include <QCheckBox>
#include <QComboBox>
#include <QDoubleSpinBox>
#include <QFormLayout>
#include <QLabel>
#include <QLineEdit>
#include <QSqlError>
#include <QSqlField>
#include <QSqlRecord>
#include <colorbutton.h>
#include <qpiconfig.h>
#include <qpiconfigvaluewidget.h>


SQLRecordWidget::SQLRecordWidget(QWidget * parent): QWidget(parent) {
	connection_name = QSqlDatabase::defaultConnection;
	setLayout(new QFormLayout());
	ro = false;
}


SQLRecordWidget::~SQLRecordWidget() {}


void SQLRecordWidget::setRecord(const QSqlRecord & q, bool full_update) {
	// qDebug() << (q.count() - hidden.size()) << (layout()->count() / 2);
	if (full_update || (q.count() - hidden.size()) != (layout()->count() / 2)) createWidgets(q);
	for (int i = 0; i < q.count(); ++i) {
		QSqlField f   = q.field(i);
		QWidget * val = 0;
		for (int w = 0; w < cws.size(); ++w)
			if (cws[w]->objectName() == f.name()) {
				val = cws[w];
				break;
			}
		if (val == 0) continue;
		if (qobject_cast<QLineEdit *>(val)) {
			if (relations.contains(f.name())) {
				bool ok = false;
				int sv(f.value().toInt(&ok));
				if (!ok)
					qobject_cast<QLineEdit *>(val)->setText("");
				else {
					QList<QPair<int, QString>> & rv(relations[f.name()]);
					for (int j = 0; j < rv.size(); ++j) {
						if (sv == rv[j].first) {
							qobject_cast<QLineEdit *>(val)->setText(rv[j].second);
							break;
						}
					}
				}
			} else
				qobject_cast<QLineEdit *>(val)->setText(f.value().toString());
		}
		if (qobject_cast<QCheckBox *>(val)) qobject_cast<QCheckBox *>(val)->setChecked(f.value().toBool());
		if (qobject_cast<QDoubleSpinBox *>(val)) qobject_cast<QDoubleSpinBox *>(val)->setValue(f.value().toDouble());
		if (qobject_cast<ColorButton *>(val)) qobject_cast<ColorButton *>(val)->setColor(QColor::fromRgba(f.value().toUInt()));
		if (qobject_cast<StringListEdit *>(val)) {
			QString s = f.value().toString();
			qobject_cast<StringListEdit *>(val)->setValue(s.isEmpty() ? QStringList() : s.split(";"));
		}
		if (qobject_cast<QComboBox *>(val)) {
			bool ok = false;
			int sv(f.value().toInt(&ok));
			if (!ok)
				((QComboBox *)val)->setCurrentIndex(-1);
			else {
				QList<QPair<int, QString>> & rv(relations[f.name()]);
				for (int j = 0; j < rv.size(); ++j) {
					if (sv == rv[j].first) {
						((QComboBox *)val)->setCurrentIndex(j);
						break;
					}
				}
			}
		}
	}
}


void SQLRecordWidget::setFixedValue(const QString & name, const QVariant & value) {
	foreach(QWidget * w, cws) {
		if (w->objectName() != name) continue;
		if (qobject_cast<QLineEdit *>(w)) {
			qobject_cast<QLineEdit *>(w)->setText(value.toString());
			qobject_cast<QLineEdit *>(w)->setReadOnly(true);
		}
		if (qobject_cast<QCheckBox *>(w)) {
			qobject_cast<QCheckBox *>(w)->setChecked(value.toBool());
			w->setEnabled(false);
		}
		if (qobject_cast<QDoubleSpinBox *>(w)) {
			qobject_cast<QDoubleSpinBox *>(w)->setValue(value.toDouble());
			qobject_cast<QDoubleSpinBox *>(w)->setReadOnly(true);
		}
		if (qobject_cast<ColorButton *>(w)) {
			qobject_cast<ColorButton *>(w)->setColor(QColor::fromRgba(value.toUInt()));
			qobject_cast<ColorButton *>(w)->setEnabled(false);
		}
		if (qobject_cast<StringListEdit *>(w)) {
			qobject_cast<StringListEdit *>(w)->setValue(QStringList());
			qobject_cast<StringListEdit *>(w)->setEnabled(false);
		}
	}
}


void SQLRecordWidget::clearValues() {
	foreach(QWidget * w, cws) {
		if (qobject_cast<QLineEdit *>(w)) qobject_cast<QLineEdit *>(w)->setText(QString());
		if (qobject_cast<QCheckBox *>(w)) qobject_cast<QCheckBox *>(w)->setChecked(false);
		if (qobject_cast<QDoubleSpinBox *>(w)) qobject_cast<QDoubleSpinBox *>(w)->setValue(0.);
		if (qobject_cast<ColorButton *>(w)) qobject_cast<ColorButton *>(w)->setColor(Qt::black);
		if (qobject_cast<StringListEdit *>(w)) qobject_cast<StringListEdit *>(w)->setValue(QStringList());
	}
}


void SQLRecordWidget::createWidgets(const QSqlRecord & q) {
	qDeleteAll(cws);
	cws.clear();
	delete layout();
	QFormLayout * lay = new QFormLayout();
	lay->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
	lay->setLabelAlignment(Qt::AlignRight);
	lay->setContentsMargins(0, 0, 0, 0);
	setLayout(lay);
	// qDebug() << "createWidgets" << q.count();
	for (int i = 0; i < q.count(); ++i) {
		QSqlField f = q.field(i);
		if (hidden.contains(f.name())) continue;
		QPair<QString, QString> ctr = trColumn(f.name());
		QWidget * val               = 0;
		if (relations.contains(f.name())) {
			if (ro) {
				val = new QLineEdit();
			} else {
				val = new QComboBox();
				QList<QPair<int, QString>> & rv(relations[f.name()]);
				bool ok = false;
				int sv(f.value().toInt(&ok));
				for (int j = 0; j < rv.size(); ++j) {
					((QComboBox *)val)->addItem(rv[j].second, rv[j].first);
					if (ok)
						if (sv == rv[j].first) ((QComboBox *)val)->setCurrentIndex(j);
				}
				if (!ok) ((QComboBox *)val)->setCurrentIndex(-1);
			}
		} else {
			switch (fieldType(f)) {
			case QVariant::Int:
			case QVariant::UInt:
			case QVariant::LongLong:
			case QVariant::ULongLong:
			case QVariant::Double:
				val = new QDoubleSpinBox();
				((QDoubleSpinBox *)val)->setRange(-1E+99, 1E+99);
				((QDoubleSpinBox *)val)->setDecimals(3);
				break;
			case QVariant::String: val = new QLineEdit(); break;
			case QVariant::Bool: val = new QCheckBox(); break;
			case QVariant::Color:
				val = new ColorButton();
				((ColorButton *)val)->setUseAlphaChannel(true);
				((ColorButton *)val)->setUseNativeDialog(true);
				break;
			case QVariant::StringList: val = new StringListEdit(); break;
			default: break;
			}
			if (f.type() == QVariant::String || f.type() == QVariant::StringList) val->setProperty("_string", true);
		}
		if (val != 0) {
			QString lt = ctr.first.left(1).toUpper() + ctr.first.right(ctr.first.length() - 1);
			while (lt.endsWith("_"))
				lt.chop(1);
			QLabel * lbl = new QLabel(lt + ":");
			lbl->setToolTip(ctr.second);
			cws << lbl;
			val->setObjectName(f.name());
			cws << val;
			lay->addRow(lbl, val);
		} else {
			if (!hidden.contains(ctr.first)) hidden << ctr.first;
		}
	}
	updateWidgets();
	emit widgetsCreated();
}


void SQLRecordWidget::updateWidgets() {
	foreach(QWidget * w, cws) {
		if (qobject_cast<QLineEdit *>(w)) qobject_cast<QLineEdit *>(w)->setReadOnly(ro);
		if (qobject_cast<QCheckBox *>(w)) qobject_cast<QCheckBox *>(w)->setEnabled(!ro);
		if (qobject_cast<QDoubleSpinBox *>(w)) qobject_cast<QDoubleSpinBox *>(w)->setReadOnly(ro);
		if (qobject_cast<ColorButton *>(w)) qobject_cast<ColorButton *>(w)->setEnabled(!ro);
		if (qobject_cast<StringListEdit *>(w)) qobject_cast<StringListEdit *>(w)->setEnabled(!ro);
		if (qobject_cast<QComboBox *>(w)) qobject_cast<QComboBox *>(w)->setEnabled(!ro);
	}
}


QVariant::Type SQLRecordWidget::fieldType(const QSqlField & f) {
	QString ft = ftypes.value(f.name());
	if (ft.isEmpty()) return f.type();
	if (ft == "l") return QVariant::StringList;
	if (ft == "b") return QVariant::Bool;
	if (ft == "n") return QVariant::Int;
	if (ft == "f") return QVariant::Double;
	if (ft == "c") return QVariant::Color;
	if (ft == "r") return QVariant::Rect;
	if (ft == "a") return QVariant::RectF;
	if (ft == "p") return QVariant::Point;
	if (ft == "v") return QVariant::PointF;
	return QVariant::String;
}


void SQLRecordWidget::addTranslation(const QString & file) {
	QPIConfig conf(file, QIODevice::ReadOnly, QPIConfig::Config);
	QPIConfig::Branch ae = conf.allLeaves();
	foreach(QPIConfig::Entry * e, ae)
		translates[e->name()] = QPair<QString, QString>(e->value(), e->comment());
}


QString SQLRecordWidget::getValuesQuery() const {
	QString ret("(");
	bool first = true;
	foreach(QWidget * w, cws) {
		QWidget * vw(0);
		if (qobject_cast<QLineEdit *>(w)) {
			if (!qobject_cast<QLineEdit *>(w)->text().isEmpty()) vw = w;
		}
		if (qobject_cast<QCheckBox *>(w)) vw = w;
		if (qobject_cast<QDoubleSpinBox *>(w)) vw = w;
		if (qobject_cast<ColorButton *>(w)) vw = w;
		if (qobject_cast<StringListEdit *>(w)) vw = w;
		if (qobject_cast<QComboBox *>(w)) vw = w;
		if (vw == 0) continue;
		if (!first) ret += ",";
		first = false;
		ret += vw->objectName();
	}
	ret += ") VALUES (";
	foreach(QWidget * w, cws) {
		QWidget * vw(0);
		if (qobject_cast<QLineEdit *>(w)) {
			if (!qobject_cast<QLineEdit *>(w)->text().isEmpty()) {
				vw     = w;
				bool q = w->property("_string").toBool();
				if (q) ret += "'";
				ret += qobject_cast<QLineEdit *>(w)->text();
				if (q) ret += "'";
			}
		}
		if (qobject_cast<QCheckBox *>(w)) {
			vw = w;
			ret += qobject_cast<QCheckBox *>(w)->isChecked() ? "1" : "0";
		}
		if (qobject_cast<QDoubleSpinBox *>(w)) {
			vw = w;
			ret += QString::number(qobject_cast<QDoubleSpinBox *>(w)->value());
		}
		if (qobject_cast<ColorButton *>(w)) {
			vw = w;
			ret += QString::number(qobject_cast<ColorButton *>(w)->color().rgba());
		}
		if (qobject_cast<StringListEdit *>(w)) {
			vw = w;
			ret += "'" + qobject_cast<StringListEdit *>(w)->value().join(";") + "'";
		}
		if (qobject_cast<QComboBox *>(w)) {
			vw = w;
			ret += ((QComboBox *)w)->itemData(((QComboBox *)w)->currentIndex(), Qt::UserRole).toString();
		}
		if (vw == 0) continue;
		ret += ",";
	}
	if (ret.endsWith(",")) ret.chop(1);
	ret += ")";
	return ret;
}


QString SQLRecordWidget::getUpdateQuery() const {
	QString ret;
	bool first = true;
	foreach(QWidget * w, cws) {
		QWidget * vw(0);
		if (qobject_cast<QLineEdit *>(w)) vw = w;
		if (qobject_cast<QCheckBox *>(w)) vw = w;
		if (qobject_cast<QDoubleSpinBox *>(w)) vw = w;
		if (qobject_cast<ColorButton *>(w)) vw = w;
		if (qobject_cast<StringListEdit *>(w)) vw = w;
		if (qobject_cast<QComboBox *>(w)) vw = w;
		if (vw == 0) continue;
		if (!first) ret += ",";
		first = false;
		ret += vw->objectName() + "=";
		if (qobject_cast<QLineEdit *>(w)) {
			if (qobject_cast<QLineEdit *>(w)->text().isEmpty())
				ret += "null";
			else {
				bool q = w->property("_string").toBool();
				if (q) ret += "'";
				ret += qobject_cast<QLineEdit *>(w)->text();
				if (q) ret += "'";
			}
		}
		if (qobject_cast<QCheckBox *>(w)) ret += qobject_cast<QCheckBox *>(w)->isChecked() ? "1" : "0";
		if (qobject_cast<QDoubleSpinBox *>(w)) ret += QString::number(qobject_cast<QDoubleSpinBox *>(w)->value());
		if (qobject_cast<ColorButton *>(w)) ret += QString::number(qobject_cast<ColorButton *>(w)->color().rgba());
		if (qobject_cast<StringListEdit *>(w)) ret += "'" + qobject_cast<StringListEdit *>(w)->value().join(";") + "'";
		if (qobject_cast<QComboBox *>(w)) {
			QString cd = ((QComboBox *)w)->itemData(((QComboBox *)w)->currentIndex(), Qt::UserRole).toString();
			ret += cd.isEmpty() ? "null" : cd;
		}
	}
	return ret;
}


bool SQLRecordWidget::isEmpty() const {
	return layout()->count() < 2;
}


void SQLRecordWidget::updateRelations() {
	relations.clear();
	QSqlDatabase db(QSqlDatabase::database(connection_name));
	if (!db.isOpen()) return;
	for (int i = 0; i < relations_src.size(); ++i) {
		QSqlQuery q(QString("SELECT %1,%2 FROM %3").arg(relations_src[i].key, relations_src[i].ocol, relations_src[i].table), db);
		QList<QPair<int, QString>> cr;
		while (q.next())
			cr << QPair<int, QString>(q.value(0).toInt(), q.value(1).toString());
		relations[relations_src[i].tcol] = cr;
	}
	QFormLayout * lay = (QFormLayout *)layout();
	if (!lay) return;
	for (int i = 0; i < lay->rowCount(); ++i) {
		QString name = lay->itemAt(i, QFormLayout::FieldRole)->widget()->objectName();
		if (hidden.contains(name)) continue;
		QComboBox * val = qobject_cast<QComboBox *>(lay->itemAt(i, QFormLayout::FieldRole)->widget());
		if (!val) continue;
		if (!relations.contains(name)) continue;
		QList<QPair<int, QString>> & rv(relations[name]);
		bool ok = false;
		int sv(val->itemData(val->currentIndex()).toInt());
		val->clear();
		for (int j = 0; j < rv.size(); ++j) {
			val->addItem(rv[j].second, rv[j].first);
			if (sv == rv[j].first) val->setCurrentIndex(j);
		}
		if (!ok) val->setCurrentIndex(-1);
	}
}
