#include "sql_table_widget.h"

#include "qad_types.h"
#include "ui_sql_table_widget.h"

#include <QApplication>
#include <QFile>
#include <QFileInfo>
#include <QItemDelegate>
#include <QTextStream>


QString SQLTableWidget::_dir;


ColumnProperties::ColumnProperties(const QString & table_,
                                   const QString & type_,
                                   const QString & name_,
                                   const QString & def_,
                                   bool auto_,
                                   bool prim_,
                                   bool option_) {
	visible        = true;
	table          = table_;
	name           = name_;
	def            = def_;
	auto_increment = auto_;
	primary        = prim_;
	optional       = option_;
	int bs = type_.indexOf("("), bf = type_.indexOf(")");
	size       = type_.mid(bs + 1, bf - bs - 1).toInt();
	key_column = 0;
	type       = typeFromString(type_.toLower());
	is_text    = (type == ColumnProperties::Chars || type == ColumnProperties::Text);
}


ColumnProperties::DataType ColumnProperties::typeFromString(const QString & n) {
	if (n.indexOf("datetime") >= 0 || n.indexOf("timestamp") >= 0) return ColumnProperties::DateTime;
	if (n.indexOf("date") >= 0) return ColumnProperties::Date;
	if (n.indexOf("time") >= 0) return ColumnProperties::Time;
	if (n.indexOf("int") >= 0) return ColumnProperties::Int;
	if (n.indexOf("char") >= 0) return ColumnProperties::Chars;
	if (n.indexOf("dec") >= 0 || n.indexOf("numeric") >= 0 || n.indexOf("real") >= 0 || n.indexOf("float") >= 0 || n.indexOf("double") >= 0)
		return ColumnProperties::Float;
	if (n.indexOf("text") >= 0) return ColumnProperties::Text;
	if (n.indexOf("blob") >= 0) return ColumnProperties::Binary;
	return ColumnProperties::Unknown;
}


QVariant SQLQueryModel::data(const QModelIndex & item, int role) const {
	QVariant var = QSqlQueryModel::data(item, role);
	// QVariant::Type t = var.type();
	/*if (t == QVariant::Date) return var.toDate().toString("dd.MM.yyyy");
	if (t == QVariant::Time) return var.toTime().toString("hh:mm:ss");
	if (t == QVariant::DateTime) return var.toDateTime().toString("dd.MM.yyyy hh:mm:ss");*/
	return var;
}


bool SQLQueryModel::setData(const QModelIndex & index, const QVariant & value, int role) {
	if (value.isValid() && role == Qt::EditRole) {
		QSqlQuery q(QSqlDatabase::database(conn_name));
		int col                      = mapColumn(index.column());
		ColumnProperties::DataType t = column_props[col].type;
		bool quotes                  = (t == ColumnProperties::Chars || t == ColumnProperties::Text || t == ColumnProperties::Date ||
                       t == ColumnProperties::Time || t == ColumnProperties::ColumnProperties::DateTime);
		QString val                  = value.toString();
		if (t == ColumnProperties::Date) val = value.toDate().toString("yyyy-MM-dd");
		if (t == ColumnProperties::Time) val = value.toTime().toString("hh:mm:ss");
		if (t == ColumnProperties::DateTime) val = value.toDateTime().toString("yyyy-MM-dd hh:mm:ss");
		if (!q.exec("UPDATE " + table_ + " SET " + column_props[col].name + "=" + (quotes ? "'" : "") + val + (quotes ? "'" : "") +
		            " WHERE " + column_props[0].name + "=" + record(index.row()).value(0).toString() + ";")) {
			qDebug() << "[UPDATE]" << q.lastQuery() << "Error:" << q.lastError();
			return false;
		}
		// qDebug() << "[UPDATE]" << q.lastQuery();
		dataChanged(index, index);
		updateTable(true);
		emit tableChanged();
		return true;
	}
	return false;
}


SQLUniEdit::SQLUniEdit(const ColumnProperties & prop, const QString & conn_name, QWidget * parent)
	: QWidget(parent)
	, prop_(prop)
	, connection_name(conn_name) {
	wtext     = 0;
	wint      = 0;
	wfloat    = 0;
	wdate     = 0;
	wtime     = 0;
	wdatetime = 0;
	wrelation = 0;
	setProp(prop_);
	w_ << (QWidget **)&wtext << (QWidget **)&wint << (QWidget **)&wfloat << (QWidget **)&wdate << (QWidget **)&wtime
	   << (QWidget **)&wdatetime << (QWidget **)&wrelation;
}


void SQLUniEdit::setProp(const ColumnProperties & p) {
	for (int i = 0; i < w_.size(); ++i)
		deleteW(*w_[i]);
	wtext     = 0;
	wint      = 0;
	wfloat    = 0;
	wdate     = 0;
	wtime     = 0;
	wdatetime = 0;
	wrelation = 0;
	prop_     = p;
	if (prop_.isRelation()) {
		wrelation = new QComboBox(this);
		wrelation->installEventFilter(this);
		for (int i = 0; i < prop_.relation_list.size(); ++i)
			wrelation->addItem(prop_.relation_list[i].second, prop_.relation_list[i].first);
		connect(wrelation, SIGNAL(currentIndexChanged(int)), this, SLOT(value_int(int)));
	} else {
		ColumnProperties::DataType t = prop_.getType();
		switch (t) {
		case ColumnProperties::Int:
			wint = new QSpinBox(this);
			wint->setRange(-0x7FFFFFFF, 0x7FFFFFFF);
			connect(wint, SIGNAL(valueChanged(int)), this, SLOT(value_int(int)));
			break;
		case ColumnProperties::Float:
			wfloat = new QDoubleSpinBox(this);
			wfloat->setRange(-1E+300, 1E+300);
			wfloat->setDecimals(2);
			connect(wfloat, SIGNAL(valueChanged(double)), this, SLOT(value_float(double)));
			break;
		case ColumnProperties::Date:
			wdate = new QDateEdit(this);
			wdate->setDisplayFormat("dd.MM.yyyy");
			wdate->setCalendarPopup(true);
			wdate->setDate(QDate::currentDate());
			connect(wdate, SIGNAL(dateChanged(QDate)), this, SLOT(value_date(QDate)));
			break;
		case ColumnProperties::Time:
			wtime = new QTimeEdit(this);
			wtime->setDisplayFormat("hh:mm:ss");
			wtime->setTime(QTime::currentTime());
			connect(wtime, SIGNAL(timeChanged(QTime)), this, SLOT(value_time(QTime)));
			break;
		case ColumnProperties::DateTime:
			wdatetime = new QDateTimeEdit(this);
			wdatetime->setDisplayFormat("dd.MM.yyyy hh:mm:ss");
			wdatetime->setCalendarPopup(true);
			wdatetime->setDateTime(QDateTime::currentDateTime());
			connect(wdatetime, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(value_datetime(QDateTime)));
			break;
		case ColumnProperties::Chars:
		case ColumnProperties::Text:
		case ColumnProperties::Unknown:
		default:
			wtext = new QLineEdit(this);
			connect(wtext, SIGNAL(textChanged(QString)), this, SLOT(value_text(QString)));
			break;
		}
	}
	for (int i = 0; i < w_.size(); ++i)
		if (*w_[i]) (*w_[i])->setHidden(false);
	resizeEvent(0);
}


QString SQLUniEdit::value(bool for_insert) {
	QString ret;
	ColumnProperties::DataType t = (for_insert ? prop_.type : prop_.getType());
	bool quotes                  = (t == ColumnProperties::Chars || t == ColumnProperties::Text || t == ColumnProperties::Date ||
                   t == ColumnProperties::Time || t == ColumnProperties::ColumnProperties::DateTime);
	if (quotes) ret = "'";
	if (prop_.isRelation())
		if (for_insert)
			ret += wrelation->itemData(wrelation->currentIndex()).toString();
		else
			ret += wrelation->currentText();
	else {
		if (wint != 0)
			ret += QString::number(wint->value());
		else {
			if (wfloat != 0)
				ret += QString::number(wfloat->value());
			else {
				if (wdate != 0)
					ret += wdate->date().toString("yyyy-MM-dd");
				else {
					if (wtime != 0)
						ret += wtime->time().toString("hh:mm:ss");
					else {
						if (wdatetime != 0)
							ret += wdatetime->dateTime().toString("yyyy-MM-dd hh:mm:ss");
						else {
							ret += wtext->text();
						}
					}
				}
			}
		}
	}
	if (quotes) ret += "'";
	if (ret == "''") ret.clear();
	return ret;
}


bool SQLUniEdit::eventFilter(QObject * o, QEvent * e) {
	if (e->type() == QEvent::MouseButtonPress) updateRelation();
	return QWidget::eventFilter(o, e);
}


void SQLUniEdit::setValue(const QVariant & value) {
	if (prop_.isRelation()) return;
	if (wint != 0)
		wint->setValue(value.toInt());
	else {
		if (wfloat != 0)
			wfloat->setValue(value.toDouble());
		else {
			if (wdate != 0)
				wdate->setDate(value.toDate());
			else {
				if (wtime != 0)
					wtime->setTime(value.toTime());
				else {
					if (wdatetime != 0)
						wdatetime->setDateTime(value.toDateTime());
					else {
						wtext->setText(value.toString());
					}
				}
			}
		}
	}
}


void SQLUniEdit::updateRelation() {
	prop_.relation_list = SQLTableWidget::getColumnValues(prop_.relation_table, prop_.relation_key, prop_.relation_column, connection_name);
	int pi              = wrelation->currentIndex();
	wrelation->blockSignals(true);
	wrelation->clear();
	for (int i = 0; i < prop_.relation_list.size(); ++i)
		wrelation->addItem(prop_.relation_list[i].second, prop_.relation_list[i].first);
	wrelation->setCurrentIndex(pi);
	wrelation->blockSignals(false);
}


SQLNewEdit::SQLNewEdit(const ColumnProperties & prop, const QString & conn_name, QWidget * parent): QWidget(parent), prop_(prop) {
	line = new SQLUniEdit(prop, conn_name);
	line->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
	check = new QCheckBox();
	check->setChecked(true);
	check->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
	connect(check, SIGNAL(toggled(bool)), line, SLOT(setEnabled(bool)));
	QLayout * lay = new QHBoxLayout();
	lay->addWidget(check);
	lay->addWidget(line);
	lay->setContentsMargins(0, 0, 0, 0);
	lay->setSpacing(0);
	setLayout(lay);
	setProp(prop);
	check->setChecked(!prop.primary);
}


void SQLNewEdit::setProp(const ColumnProperties & p) {
	prop_ = p;
	line->setProp(p);
	check->setEnabled(p.optional);
	if (!check->isEnabled()) check->setChecked(true);
}


SQLFilterEdit::SQLFilterEdit(const ColumnProperties & prop, const QString & conn_name, QWidget * parent): QWidget(parent), prop_(prop) {
	line = new SQLUniEdit(prop, conn_name);
	line->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
	combo = new QComboBox();
	combo->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
	QStringList sl;
	sl << " "
	   << "=" << QString::fromUtf8("≈") << QString::fromUtf8("≠") << ">" << QString::fromUtf8("≥") << "<" << QString::fromUtf8("≤");
	combo->addItems(sl);
	combo->setCurrentIndex(0);
	connect(line, SIGNAL(valueChanged(QVariant)), this, SIGNAL(filterChanged()));
	connect(line, SIGNAL(valueChanged(QVariant)), this, SLOT(value_changed()));
	connect(combo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(filterChanged()));
	QLayout * lay = new QHBoxLayout();
	lay->addWidget(combo);
	lay->addWidget(line);
	lay->setContentsMargins(0, 0, 0, 0);
	lay->setSpacing(2);
	setLayout(lay);
	setProp(prop);
}


QString SQLFilterEdit::filter() const {
	QString ret;
	if (combo->currentIndex() == 0) return ret;
	ret = prop_.fullName();
	switch (combo->currentIndex()) {
	case 1: ret.append("="); break;
	case 2: ret.append("="); break;
	case 3: ret.append("!="); break;
	case 4: ret.append(">"); break;
	case 5: ret.append(">="); break;
	case 6: ret.append("<"); break;
	case 7: ret.append("<="); break;
	};
	QString val = line->value();
	if (prop_.is_text && combo->currentIndex() == 2) {
		ret.chop(1);
		ret.append(" LIKE '%");
		ret.append(val.mid(1, val.length() - 2));
		ret.append("%'");
	} else
		ret.append(val);
	return ret;
}


void SQLFilterEdit::clear() {
	line->setValue(QVariant());
	combo->setCurrentIndex(0);
}


SQLItemDelegate::SQLItemDelegate(QList<ColumnProperties> & cp, QVector<int> & ci, bool & ro, const QString & conn_name, QObject * parent)
	: QStyledItemDelegate(parent)
	, column_props(cp)
	, column_indexes(ci)
	, read_only(ro)
	, connection_name(conn_name) {
	;
}


QWidget * SQLItemDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {
	if (read_only) return 0;
	ColumnProperties & cp(column_props[mapColumn(index.column())]);
	if (cp.isRelation()) {
		cp.relation_list = SQLTableWidget::getColumnValues(cp.relation_table, cp.relation_key, cp.relation_column, connection_name);
		QComboBox * w    = new QComboBox(parent);
		w->setGeometry(option.rect);
		for (int i = 0; i < cp.relation_list.size(); ++i) {
			w->addItem(cp.relation_list[i].second, cp.relation_list[i].first);
			if (index.data().toString() == cp.relation_list[i].second) w->setCurrentIndex(i);
		}
		return w;
	} else {
		QWidget * w = QStyledItemDelegate::createEditor(parent, option, index);
		if (qobject_cast<QTimeEdit *>(w)) {
			qobject_cast<QTimeEdit *>(w)->setDisplayFormat("hh:mm:ss");
		} else if (qobject_cast<QDateEdit *>(w)) {
			qobject_cast<QDateEdit *>(w)->setCalendarPopup(true);
			qobject_cast<QDateEdit *>(w)->setDisplayFormat("dd.MM.yyyy");
		} else if (qobject_cast<QDateTimeEdit *>(w)) {
			qobject_cast<QDateTimeEdit *>(w)->setCalendarPopup(true);
			qobject_cast<QDateTimeEdit *>(w)->setDisplayFormat("dd.MM.yyyy hh:mm:ss");
		}
		return w;
	}
	return 0;
}


QString SQLItemDelegate::displayText(const QVariant & value, const QLocale & locale) const {
	QVariant::Type t = value.type();
	if (t == QVariant::Date) return value.toDate().toString("dd.MM.yyyy");
	if (t == QVariant::Time) return value.toTime().toString("hh:mm:ss");
	if (t == QVariant::DateTime) return value.toDateTime().toString("dd.MM.yyyy hh:mm:ss");
	return QStyledItemDelegate::displayText(value, locale);
}


void SQLItemDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
	if (qobject_cast<QComboBox *>(editor)) {
		model->setData(index, qobject_cast<QComboBox *>(editor)->itemData(qobject_cast<QComboBox *>(editor)->currentIndex()));
		return;
	}
	QStyledItemDelegate::setModelData(editor, model, index);
}


SQLTableWidget::SQLTableWidget(QWidget * parent): QWidget(parent), filters_group(this) {
	ui = new Ui::SQLTableWidget();
	ui->setupUi(this);
	model           = 0;
	timer           = 0;
	connection_name = QSqlDatabase::defaultConnection;
	filters_active = first_update = debug = true;
	table_opened = read_only = false;
	ui->labelNew->setFixedSize(preferredIconSize(1.2, this));
	ui->labelFilter->setFixedSize(preferredIconSize(1.2, this));
	ui->view->installEventFilter(this);
	ui->view->viewport()->installEventFilter(this);
	ui->view->horizontalHeader()->setSortIndicator(0, Qt::AscendingOrder);
	ui->view->horizontalHeader()->viewport()->installEventFilter(this);
	ui->view->setItemDelegate(new SQLItemDelegate(column_props, column_indexes, read_only, connection_name));
	ui->view->horizontalHeader()->setItemDelegate(new QItemDelegate());
	ui->view->verticalHeader()->setDefaultSectionSize(fontHeight(this) * 1.5);
	// qDebug() << view->horizontalHeader()->itemDelegate();
	// qDebug() << fontMetrics().elidedText(, Qt::ElideNone, 30, Qt::TextWordWrap);
	setTableVisible(false);
	connect(ui->view->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), this, SLOT(header_sectionResized(int, int, int)));
	connect(ui->view->horizontalHeader(),
	        SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)),
	        this,
	        SLOT(header_sortIndicatorChanged(int, Qt::SortOrder)));
	connect(ui->view->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(scrolled(int)));


	// act_add.setText(tr("Add ..."));
	// act_del.setText(tr("Remove selected"));
	action_del = new QAction(QIcon(":/icons/edit-delete.png"), tr("Remove selected"), this);
	connect(action_del, SIGNAL(triggered(bool)), this, SLOT(del_triggered()));
	popup_menu.addAction(action_del);
	ui->actionFilter_AND->setChecked(true);
	filters_group.addAction(ui->actionFilter_AND);
	filters_group.addAction(ui->actionFilter_OR);
	connect(&filters_group, SIGNAL(triggered(QAction *)), this, SLOT(updateTable()));
	popup_filter.addAction(ui->actionFiltersClear);
	popup_filter.addSeparator();
	popup_filter.addAction(ui->actionFilter_AND);
	popup_filter.addAction(ui->actionFilter_OR);
	ui->buttonFilterConfig->setMenu(&popup_filter);
	// connect(&act_add, SIGNAL(triggered(bool)), this, SLOT(add_triggered()));
	// connect(&act_del, SIGNAL(triggered(bool)), this, SLOT(del_triggered()));
}


SQLTableWidget::~SQLTableWidget() {
	stopTimer();
	if (model != 0) delete model;
}


bool SQLTableWidget::eventFilter(QObject * o, QEvent * e) {
	if (o == ui->view) {
		if (e->type() == QEvent::KeyPress) {
			QKeyEvent * ke = (QKeyEvent *)e;
			if (ke->key() == Qt::Key_Home) {
				ui->view->selectRow(0);
				return true;
			}
			if (ke->key() == Qt::Key_End) {
				ui->view->selectRow(ui->view->verticalHeader()->count() - 1);
				return true;
			}
		}
	}
	if (o == ui->view->viewport()) {
		if (e->type() == QEvent::MouseButtonPress) {
			QMouseEvent * me = (QMouseEvent *)e;
			if (me->button() == Qt::RightButton) {
				action_del->setVisible(!read_only);
				popup_menu.popup(me->globalPos());
			}
		}
	}
	if (o == ui->view->horizontalHeader()->viewport()) {
		QMouseEvent * me = (QMouseEvent *)e;
		if (e->type() == QEvent::MouseButtonPress) pp = me->pos();
		if (e->type() == QEvent::MouseButtonRelease) {
			if ((me->pos() - pp).manhattanLength() < QApplication::startDragDistance()) {
				if (me->button() == Qt::LeftButton) {
					int ci = ui->view->horizontalHeader()->logicalIndexAt(me->pos()),
						ri = ui->view->horizontalHeader()->logicalIndexAt(me->pos() + QPoint(3, 0)),
						li = ui->view->horizontalHeader()->logicalIndexAt(me->pos() - QPoint(3, 0));
					// qDebug() << ci << ri << li;
					if ((ci < 0) || (ci >= ui->view->horizontalHeader()->count()) || (ci != ri) || (ci != li) ||
					    (tm.elapsed() <= QApplication::doubleClickInterval())) {
						tm.restart();
						return QObject::eventFilter(o, e);
					}
					tm.restart();
					Qt::SortOrder o = Qt::AscendingOrder;
					if (ui->view->horizontalHeader()->sortIndicatorSection() == ci &&
					    ui->view->horizontalHeader()->sortIndicatorOrder() == Qt::AscendingOrder)
						o = Qt::DescendingOrder;
					ui->view->horizontalHeader()->setSortIndicator(ci, o);
					return true;
				}
				if (me->button() == Qt::RightButton) {
					popup_col.clear();
					for (int i = 0; i < column_props.size(); ++i) {
						QPair<QString, QString> ctr = trColumn(column_props[i].name);
						QAction * a                 = popup_col.addAction(ctr.first, this, SLOT(column_triggered(bool)));
						a->setToolTip(ctr.second);
						a->setCheckable(true);
						a->setChecked(column_props[i].visible);
						a->setData(i);
					}
					popup_col.popup(me->globalPos());
					return true;
				}
			} else
				return true;
		}
	}
	return QWidget::eventFilter(o, e);
}


void SQLTableWidget::timerEvent(QTimerEvent *) {
	setTableName(table_);
}


void SQLTableWidget::changeEvent(QEvent * e) {
	QWidget::changeEvent(e);
	switch (e->type()) {
	case QEvent::LanguageChange:
		ui->retranslateUi(this);
		action_del->setText(tr("Remove selected"));
		break;
	default: break;
	}
}


void SQLTableWidget::setTableName(const QString & t) {
	ui->labelEmpty->setText(tr("Table \"%1\" doesn`t exists!").arg(t));
	stopTimer();
	table_opened = false;
	first_update = true;
	table_       = t;
	if (!QSqlDatabase::database(connection_name).isOpen()) {
		if (debug) qDebug() << "[setTableName] Database in not opened!";
		setTableVisible(false);
		timer = startTimer(1000);
		return;
	}
	model = new SQLQueryModel(table_, connection_name, column_props, column_indexes, this);
	connect(model, SIGNAL(updateTable(bool)), this, SLOT(updateTable(bool)));
	connect(model, SIGNAL(tableChanged()), this, SIGNAL(tableChanged()));
	// model->setTable(table_);
	cquery = "SELECT * FROM " + table_ + ";";
	model->setQuery(cquery, QSqlDatabase::database(connection_name));
	ui->view->setModel(model);
	connect(ui->view->selectionModel(),
	        SIGNAL(currentRowChanged(QModelIndex, QModelIndex)),
	        this,
	        SIGNAL(selectionChanged()),
	        Qt::UniqueConnection);
	//((QSqlQueryModel * )model)->setQuery(QSqlQuery("SELECT id FROM " + table_ + ";"));
	setTableVisible(checkTable());
	updateTable();
	foreach(const QString & wr, wait_rels) {
		QStringList wrl = wr.split(";");
		addRelation(wrl[0], wrl[1], wrl[2], wrl[3]);
	}
	QMapIterator<QString, bool> cnit(col_vis);
	while (cnit.hasNext()) {
		cnit.next();
		setColumnVisible(cnit.key(), cnit.value());
	}
}


bool SQLTableWidget::checkTable() {
	table_opened    = false;
	QSqlDatabase db = QSqlDatabase::database(connection_name);
	if (db.driver() == 0) return false;
	bool te = db.tables(QSql::Tables).contains(table_, Qt::CaseInsensitive);
	// qDebug() << "tables:" << db.tables(QSql::Tables) << table_ << table_opened;
	if (!te) {
		timer = startTimer(1000);
		return false;
	}
	/*if (!q.exec("SHOW TABLES")) {
	    qDebug() << "[SHOW TABLES]" << q.lastError();
	    timer = startTimer(1000);
	    return false;
	}
	while (q.next()) {
	    //qDebug() << q.value(0);
	    if (q.value(0) == table_)
	        {table_opened = true; break;}
	}
	if (!table_opened) {
	    timer = startTimer(1000);
	    return false;
	}*/
	QSqlRecord header = db.record(table_);
	QString pin       = db.primaryIndex(table_).fieldName(0);
	table_opened      = true;
	column_props.clear();
	for (int i = 0; i < header.count(); ++i) {
		QSqlField f = header.field(i);
		column_props << ColumnProperties(table_,
		                                 f.type() == QVariant::String ? "text" : QVariant::typeToName(f.type()),
		                                 f.name(),
		                                 f.defaultValue().toString(),
		                                 f.isAutoValue(),
		                                 f.name() == pin,
		                                 f.requiredStatus() == QSqlField::Optional);
	}
	/*if (!q.exec("DESCRIBE " + table_ + ";")) {
	    qDebug() << "[DESCRIBE " + table_ + "]" << q.lastError();
	    timer = startTimer(1000);
	    return false;
	}
	while (q.next()) {
	    column_props << ColumnProperties(table_,
	                                     q.value(1).toString().toLower(),
	                                     q.value(0).toString().toLower(),
	                                     q.value(4).toString().toLower(),
	                                     q.value(5).toString().toLower().indexOf("auto_increment") >= 0,
	                                     q.value(3).toString().toLower().indexOf("pri") >= 0);
	}*/
	// ui->labelNew->setFixedWidth(qMax<int>(ui->view->verticalHeader()->sizeHint().width() + 2, 16));
	// ui->labelFilter->setFixedWidth(qMax<int>(ui->view->verticalHeader()->sizeHint().width() + 2, 16));
	ui->scrollAreaNew->setFixedHeight(QLineEdit().sizeHint().height() + 2);
	ui->scrollAreaFilter->setFixedHeight(QLineEdit().sizeHint().height() + 2);
	qDeleteAll(column_news);
	qDeleteAll(column_filters);
	column_news.clear();
	column_filters.clear();
	SQLNewEdit * le = 0;
	for (int i = 0; i < column_props.size(); ++i) {
		le = new SQLNewEdit(column_props[i], connection_name);
		le->setFixedWidth(ui->view->horizontalHeader()->sectionSize(i));
		column_news.push_back(le);
		ui->layoutNew->insertWidget(i, le);
	}
	for (int i = 0; i < column_props.size(); ++i) {
		SQLFilterEdit * fe = new SQLFilterEdit(column_props[i], connection_name);
		fe->setFixedWidth(ui->view->horizontalHeader()->sectionSize(i));
		column_filters.push_back(fe);
		ui->layoutFilter->insertWidget(i, fe);
		connect(fe, SIGNAL(filterChanged()), this, SLOT(updateTable()));
	}
	return true;
}


QStringList SQLTableWidget::getTableColumns(const QString & t) {
	QStringList ret;
	QSqlDatabase db   = QSqlDatabase::database(connection_name);
	QSqlRecord header = db.record(t);
	for (int i = 0; i < header.count(); ++i)
		ret << header.field(i).name();
	return ret;
}


QString SQLTableWidget::getColumnType(const QString & t, const QString & c) {
	QSqlDatabase db   = QSqlDatabase::database(connection_name);
	QSqlRecord header = db.record(t);
	for (int i = 0; i < header.count(); ++i) {
		QSqlField f = header.field(i);
		if (f.name() != c) continue;
		return (f.type() == QVariant::String ? "text" : QVariant::typeToName(f.type()));
	}
	return QString();
}


QList<QPair<QString, QString>>
SQLTableWidget::getColumnValues(const QString & t, const QString & k, const QString & c, const QString & conn_name) {
	QList<QPair<QString, QString>> ret;
	QSqlQuery q(QSqlDatabase::database(conn_name));
	if (!q.exec("SELECT " + t + "." + k + "," + t + "." + c + " FROM " + t + ";")) return ret;
	while (q.next())
		ret << QPair<QString, QString>(q.value(0).toString(), q.value(1).toString());
	return ret;
}


QPair<QString, QString> SQLTableWidget::trColumn(const QString & n) {
	QPair<QString, QString> ftr = fixed_translates.value(n);
	if (!ftr.first.isEmpty()) return ftr;
	QPair<QString, QString> trn = translates.value(n);
	if (trn.first.isEmpty()) return QPair<QString, QString>(n, "");
	return trn;
}


void SQLTableWidget::updateTable(bool save_selection) {
	if (!filters_active || !table_opened) return;
	int vp = ui->view->verticalScrollBar()->value();
	int hp = ui->view->horizontalScrollBar()->value();
	// bool focus = view->hasFocus();
	QModelIndex csi;
	QModelIndexList sl;
	if (save_selection) {
		csi = ui->view->selectionModel()->currentIndex();
		sl  = ui->view->selectionModel()->selectedRows();
	}
	if (custom_query.isEmpty()) {
		QString cr = columnRelations(), cf = columnFilters();
		bool where = !cr.isEmpty() || !cf.isEmpty(), and_ = !cr.isEmpty() && !cf.isEmpty();
		cquery = "SELECT " + columnNames() + " FROM " + tableNames() + (where ? " WHERE " : "") + cr + (and_ ? " AND " : "") + cf +
		         " ORDER BY " + column_props[ui->view->horizontalHeader()->sortIndicatorSection()].fullName() +
		         (ui->view->horizontalHeader()->sortIndicatorOrder() == Qt::DescendingOrder ? " DESC" : " ASC") + ";";
	} else
		cquery = custom_query;
	// qDebug() << cquery;
	model->setQuery(cquery, QSqlDatabase::database(connection_name));
	if (model->lastError().isValid()) qDebug() << model->lastError();
	if (first_update) {
		ui->view->resizeColumnsToContents();
		for (int i = 0; i < ui->view->horizontalHeader()->count(); ++i)
			if (ui->view->horizontalHeader()->sectionSize(i) < 100) ui->view->horizontalHeader()->resizeSection(i, 100);
		first_update = false;
	}
	// ui->labelNew->setFixedWidth(qMax<int>(ui->view->verticalHeader()->sizeHint().width() + 2, 16));
	// ui->labelFilter->setFixedWidth(qMax<int>(ui->view->verticalHeader()->sizeHint().width() + 2, 16));
	ui->layoutNew->invalidate();
	if (save_selection) {
		QItemSelectionModel * sm = ui->view->selectionModel();
		foreach(const QModelIndex & i, sl)
			sm->select(i, QItemSelectionModel::Select | QItemSelectionModel::Rows);
		sm->setCurrentIndex(csi, QItemSelectionModel::Select);
		// ui->view->setFocus();
	}
	if (custom_query.isEmpty()) {
		for (int i = 0; i < model->columnCount(); ++i) {
			QPair<QString, QString> ctr = trColumn(column_props[mapColumn(i)].name);
			model->setHeaderData(i, Qt::Horizontal, ctr.first, Qt::DisplayRole);
			model->setHeaderData(i, Qt::Horizontal, ctr.second, Qt::ToolTipRole);
		}
	} else {
		if (custom_col_names.isEmpty()) {
			QSqlRecord header = model->record();
			for (int i = 0; i < header.count(); ++i) {
				QSqlField f                 = header.field(i);
				QPair<QString, QString> ctr = trColumn(f.name());
				model->setHeaderData(i, Qt::Horizontal, ctr.first, Qt::DisplayRole);
				model->setHeaderData(i, Qt::Horizontal, ctr.second, Qt::ToolTipRole);
			}
		} else {
			for (int i = 0; i < qMin<int>(custom_col_names.size(), model->columnCount()); ++i) {
				QPair<QString, QString> ctr = trColumn(custom_col_names[i]);
				model->setHeaderData(i, Qt::Horizontal, ctr.first, Qt::DisplayRole);
				model->setHeaderData(i, Qt::Horizontal, ctr.second, Qt::ToolTipRole);
			}
		}
	}
	ui->view->verticalScrollBar()->setValue(vp);
	ui->view->horizontalScrollBar()->setValue(hp);
	// if (focus) view->setFocus();
}


void SQLTableWidget::setConnectionName(const QString & conn_name) {
	connection_name = conn_name;
	ui->view->setItemDelegate(new SQLItemDelegate(column_props, column_indexes, read_only, connection_name));
}


QString SQLTableWidget::tableNames() {
	QString ret = table_;
	QSet<QString> rtables;
	for (int i = 0; i < column_props.size(); ++i) {
		ColumnProperties & cp(column_props[i]);
		if (!cp.visible || cp.relation_key.isEmpty() || cp.relation_column.isEmpty()) continue;
		if (rtables.contains(cp.relation_table)) continue;
		rtables << cp.relation_table;
		ret.append("," + cp.relation_table);
	}
	return ret;
}


QString SQLTableWidget::columnNames() {
	QString ret;
	bool first = true;
	column_indexes.clear();
	for (int i = 0; i < column_props.size(); ++i) {
		ColumnProperties & cp(column_props[i]);
		column_news[i]->setVisible(cp.visible);
		column_filters[i]->setVisible(cp.visible);
		if (!cp.visible && !cp.primary) continue;
		if (!first) ret.append(",");
		first = false;
		ret.append(cp.fullName());
		if (i > 0) column_indexes.push_back(i);
	}
	// qDebug() << column_indexes;
	return ret;
}


QString SQLTableWidget::columnFilters() {
	QString ret;
	bool and_ = ui->actionFilter_AND->isChecked();
	for (int i = 0; i < column_props.size(); ++i) {
		if (!column_filters[i]->isVisible() || column_filters[i]->isEmpty()) continue;
		if (!ret.isEmpty()) ret.append(and_ ? " AND " : " OR ");
		ret.append(column_filters[i]->filter());
	}
	if (!ret.isEmpty()) {
		ret.prepend("(");
		ret.append(")");
	}
	return ret;
}


QString SQLTableWidget::columnRelations() {
	QString ret;
	for (int i = 0; i < column_props.size(); ++i) {
		ColumnProperties & cp(column_props[i]);
		if (!cp.visible || cp.relation_key.isEmpty() || cp.relation_column.isEmpty()) continue;
		if (!ret.isEmpty()) ret.append(" AND ");
		ret.append(table_ + "." + cp.name + "=");
		ret.append(cp.relation_table + "." + cp.relation_key);
	}
	if (!ret.isEmpty()) {
		ret.prepend("(");
		ret.append(")");
	}
	return ret;
}


void SQLTableWidget::on_buttonAdd_clicked() {
	QStringList values;
	bool at_least = false;
	for (int i = 0; i < column_props.size(); ++i) {
		values.append(column_news[i]->value());
		if (!values.back().isEmpty()) at_least = true;
	}
	if (!at_least) return;
	QString qs("INSERT INTO " + table_ + " (");
	bool first = true;
	for (int i = 0; i < column_props.size(); ++i) {
		if (values[i].isEmpty() || !column_news[i]->isEnabled()) continue;
		if (!first) qs.append(",");
		qs.append(column_props[i].name);
		first = false;
	}
	qs.append(") VALUES(");
	first = true;
	for (int i = 0; i < column_props.size(); ++i) {
		if (values[i].isEmpty() || !column_news[i]->isEnabled()) continue;
		if (!first) qs.append(",");
		qs.append(values[i]);
		first = false;
	}
	qs.append(");");
	QSqlQuery q(QSqlDatabase::database(connection_name));
	if (q.exec(qs)) {
		updateTable();
		emit tableChanged();
	} else
		qDebug() << q.lastError();
	// qDebug() << q.lastQuery();
}


void SQLTableWidget::on_actionFiltersClear_triggered() {
	filters_active = false;
	foreach(SQLFilterEdit * l, column_filters)
		l->clear();
	filters_active = true;
	updateTable();
}


void SQLTableWidget::on_view_clicked(const QModelIndex & index) {
	emit rowClicked(index.row());
	emit recordClicked(model->record(index.row()));
}


void SQLTableWidget::del_triggered() {
	QModelIndexList si = ui->view->selectionModel()->selectedIndexes();
	QSet<int> ids;
	foreach(const QModelIndex & i, si)
		ids << model->index(i.row(), 0).data().toInt();
	if (ids.isEmpty()) return;
	QString qs("DELETE FROM " + table_ + " WHERE " + column_props[0].name + " IN (");
	bool first = true;
	foreach(int i, ids) {
		if (!first) qs.append(",");
		qs.append(QString::number(i));
		first = false;
	}
	qs.append(");");
	// qDebug() << qs; return;
	QSqlQuery q(QSqlDatabase::database(connection_name));
	if (q.exec(qs)) {
		updateTable();
		emit tableChanged();
	} else
		qDebug() << q.lastError();
}


void SQLTableWidget::setColumnVisible(int ind, bool visible) {
	if (!columnExists(ind)) return;
	column_props[ind].visible = visible;
	if (ind == 0) {
		ui->view->setColumnHidden(0, !visible);
		if (visible) ui->view->horizontalHeader()->resizeSection(0, ui->view->horizontalHeader()->defaultSectionSize());
	}
	// qDebug() << ind << on;
	updateTable();
	for (int i = 0; i < ui->view->horizontalHeader()->count(); ++i) {
		column_news[mapColumn(i)]->setFixedWidth(ui->view->horizontalHeader()->sectionSize(i));
		column_filters[mapColumn(i)]->setFixedWidth(ui->view->horizontalHeader()->sectionSize(i));
	}
}


bool SQLTableWidget::addRelation(const QString & this_column,
                                 const QString & other_table,
                                 const QString & other_key,
                                 const QString & other_column) {
	QString wr = this_column + ";" + other_table + ";" + other_key + ";" + other_column;
	if (!wait_rels.contains(wr)) wait_rels << wr;
	ColumnProperties * cp = const_cast<ColumnProperties *>(columnProperty(this_column));
	if (cp == 0) return false;
	cp->relation_table  = other_table;
	cp->relation_key    = other_key;
	cp->relation_column = other_column;
	cp->relation_type   = ColumnProperties::typeFromString(getColumnType(other_table, other_column));
	cp->relation_list   = getColumnValues(other_table, other_key, other_column, connection_name);
	for (int i = 0; i < column_props.size(); ++i)
		column_news[i]->setProp(column_props[i]);
	for (int i = 0; i < column_props.size(); ++i)
		column_filters[i]->setProp(column_props[i]);
	updateTable();
	return true;
}


void SQLTableWidget::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());
}


void SQLTableWidget::addFixedColumnTranslation(const QString & col_name, const QString & col_tr, const QString & col_tt) {
	fixed_translates[col_name] = QPair<QString, QString>(col_tr, col_tt);
	updateTable();
}


void SQLTableWidget::selectId(int id) {
	if (!model) return;
	for (int i = 0; i < model->rowCount(); ++i)
		if (model->data(model->index(i, 0)).toInt() == id) {
			ui->view->selectRow(i);
			break;
		}
}


void SQLTableWidget::setAdditionalActions(QList<QAction *> a) {
	foreach(QAction * i, add_actions)
		popup_menu.removeAction(i);
	add_actions = a;
	popup_menu.addActions(a);
}


void SQLTableWidget::setCustomQuery(const QString & q) {
	custom_query = q;
	updateTable();
}


QString SQLTableWidget::preprocessScript(QString text) {
	QTextStream s(&text, QIODevice::ReadOnly);
	QString out;
	int ce = 0;
	while (!s.atEnd()) {
		QString line = s.readLine();
		ce           = line.indexOf("--");
		if (ce >= 0) line.chop(line.length() - ce);
		out.append(line).append("\n");
	}
	int cs = out.indexOf("/*");
	while (cs >= 0) {
		ce = out.indexOf("*/", cs);
		if (ce > cs) out.remove(cs, ce - cs + 2);
		cs = out.indexOf("/*");
	}
	// qDebug() << out;
	return out;
}


bool SQLTableWidget::executeScript(const QString & text_, QSqlDatabase db, bool skip_errors, bool sqlite) {
	QString text = preprocessScript(text_);
	int ls = 0, le = text.indexOf(';');
	QSqlQuery q(db);
	db.transaction();
	while (le > 0) {
		QString line = text.mid(ls, le - ls + 1).trimmed(), fword;
		if (sqlite) line.replace("auto_increment", "autoincrement");
		QTextStream(&line, QIODevice::ReadOnly) >> fword;
		fword = fword.trimmed().toLower();
		if (fword == "exec" || fword == "execute") {
			line = line.right(line.length() - fword.length()).trimmed();
			if (line.endsWith(";")) line.chop(1);
			line = line.trimmed();
			if (line.endsWith("'") || line.endsWith("\"")) line.chop(1);
			if (line.startsWith("'") || line.startsWith("\"")) line.remove(0, 1);
			executeScriptFile(line, db, skip_errors, sqlite);
		} else {
			if (!q.exec(line)) {
				qDebug() << q.lastError();
				if (!skip_errors) return false;
			}
		}
		ls = le + 1;
		le = text.indexOf(';', ls);
	}
	db.commit();
	return true;
}


bool SQLTableWidget::executeScriptFile(const QString & file, QSqlDatabase db, bool skip_errors, bool sqlite) {
	QFile f(_dir + file);
	if (!f.open(QIODevice::ReadOnly)) {
		qDebug() << "Can`t open file" << file << "!";
		return false;
	}
	QString pd    = _dir;
	_dir          = QFileInfo(f).absolutePath() + "/";
	QByteArray ba = f.readAll();
	bool ok       = executeScript(QString::fromUtf8(ba.data(), ba.size()), db, skip_errors, sqlite);
	f.close();
	_dir = pd;
	return ok;
}


void SQLTableWidget::column_triggered(bool on) {
	setColumnVisible(((QAction *)sender())->data().toInt(), on);
}


void SQLTableWidget::header_sectionResized(int logicalIndex, int oldSize, int newSize) {
	column_news[mapColumn(logicalIndex)]->setFixedWidth(newSize);
	column_filters[mapColumn(logicalIndex)]->setFixedWidth(newSize);
}


void SQLTableWidget::header_sortIndicatorChanged(int logicalIndex, Qt::SortOrder order) {
	updateTable();
}


bool SQLTableWidget::connectToDatabase(const QString & config, const QString & conn_name) {
	bool ok = true;
	{
		QPIConfig conf(config, QIODevice::ReadOnly, QPIConfig::Config);
		if (!conf.isOpen())
			ok = false;
		else {
			QSqlDatabase base = QSqlDatabase::addDatabase(conf.getValue("driver", "QMYSQL").toString(), conn_name);
			if (!base.isValid()) {
				qDebug() << "[QSqlDatabase::addDatabase] Error:" << base.lastError();
				ok = false;
			} else {
				base.setHostName(conf.getValue("host").toString());
				base.setPort(conf.getValue("port", -1).toInt());
				base.setDatabaseName(conf.getValue("database").toString());
				if (!base.open(conf.getValue("login").toString(), conf.getValue("password").toString())) {
					qDebug() << "[QSqlDatabase::open] Error:" << base.lastError();
					ok = false;
				}
			}
		}
	}
	if (!ok) QSqlDatabase::removeDatabase(conn_name);
	/*QSqlQuery q;
	QString dbname = conf.getValue("database", "").stringValue();
	bool dbex = false;
	if (!q.exec("SHOW DATABASES;")) {
	    qDebug() << "[SHOW DATABASES]" << q.lastError();
	    return false;
	}
	while (q.next())
	    if (q.value(0) == dbname)
	        {dbex = true; break;}
	if (!dbex) {
	    if (!q.exec("CREATE DATABASE " + dbname + ";")) {
	        qDebug() << "[CREATE DATABASE]" << q.lastError();
	        return false;
	    }
	}
	if (!q.exec("USE " + dbname + ";")) {
	    qDebug() << "[USE]" << q.lastError();
	    return false;
	}*/
	return ok;
}


QTableView * SQLTableWidget::tableView() {
	return ui->view;
}


bool SQLTableWidget::lineNewVisible() const {
	return ui->widgetNew->isVisibleTo(ui->pageTable);
}


bool SQLTableWidget::lineFilterVisible() const {
	return ui->widgetFilter->isVisibleTo(ui->pageTable);
}


QSqlRecord SQLTableWidget::currentRecord() const {
	if (model) return model->record(ui->view->currentIndex().row());
	return QSqlRecord();
}


void SQLTableWidget::setTableVisible(bool on) {
	ui->stackedWidget->setCurrentIndex(on ? 1 : 0);
}


void SQLTableWidget::setLineNewVisible(bool on) {
	ui->widgetNew->setVisible(on);
	ui->buttonAdd->setVisible(on);
}


void SQLTableWidget::setLineFilterVisible(bool on) {
	ui->widgetFilter->setVisible(on);
	ui->buttonFilterConfig->setVisible(on);
}


void SQLTableWidget::setReadOnly(bool yes) {
	read_only = yes;
	ui->scrollAreaNew->setDisabled(yes);
	ui->buttonAdd->setDisabled(yes);
}


void SQLTableWidget::scrolled(int value) {
	ui->scrollAreaNew->horizontalScrollBar()->setValue(value);
	ui->scrollAreaFilter->horizontalScrollBar()->setValue(value);
}
