#include <QMessageBox>
#include <QUiLoader>
#include <QMdiSubWindow>
#include <QMetaObject>
#include <QMetaMethod>
#include <QWidgetAction>
#include <QClipboard>
#include <QMenu>
#include <QTimer>
#include "SH_ui_sync.h"
#include "form_widget.h"
#include <pifile.h>

#include "button.h"
#include "slider.h"
#include "qtimerwidget.h"

Q_DECLARE_METATYPE(QMetaMethod)
const char qt_new_form[] =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
<ui version=\"4.0\">\
 <class>%1</class>\
 <widget class=\"QWidget\" name=\"%2\">\
  <property name=\"geometry\">\
   <rect>\
	<x>0</x>\
	<y>0</y>\
	<width>400</width>\
	<height>300</height>\
   </rect>\
  </property>\
  <property name=\"windowTitle\">\
   <string>%3</string>\
  </property>\
 </widget>\
 <resources/>\
 <connections/>\
</ui>";


FormWidget::FormWidget(QWidget * parent): QWidget(parent), Ui::FormWidget() {
	qRegisterMetaType<QMetaMethod>("QMetaMethod");
	setupUi(this);
	form = tform = 0;
	connect(CORE, SIGNAL(stopTest()), this, SLOT(stopTest()));
	double new_font_pt = 12, old_font_pt = font().pointSizeF();
	setStyleSheet(QString("font: %1pt \"DejaVu Sans\";").arg(qRound(new_font_pt)));
	RUNTIME->font_scale = new_font_pt / old_font_pt;
	setStyleSheet("");
}


bool FormWidget::loadForTest() {
	if (RUNTIME->server_project.ui.isEmpty()) return false;
	QFile f(RUNTIME->server_project.ui);
	f.open(QIODevice::ReadOnly);
	tform = ui_loader.load(&f);
	if (!tform) return false;
	tform->setStyleSheet(QString("font: 12pt \"DejaVu Sans\";"));
	tform->setWindowModality(Qt::ApplicationModal);
	tform->adjustSize();
	RUNTIME->json_ui = SHUISync::widget2JSON(&ui_loader);
	return true;
}


void FormWidget::startTest() {
	if (!tform) return;
	tform->installEventFilter(this);
	tform->show();
}


bool FormWidget::eventFilter(QObject * o, QEvent * e) {
	//qDebug() << o << e;
	if (o == tform) {
		if (e->type() == QEvent::Close) {
			emit CORE->stopTest();
		}
		return QWidget::eventFilter(o, e);
	}
	if (o == form) {
		if (e->type() == QEvent::Close) {
			CORE->widgetScripts->clearForm();
		}
		return QWidget::eventFilter(o, e);
	}
	if (e->type() == QEvent::MouseButtonPress) {
		if (((QMouseEvent*)e)->button() == Qt::RightButton) {
			invokeSignalPopup(((QWidget*)o)->childAt(((QMouseEvent*)e)->pos()), ((QMouseEvent*)e)->globalPos());
			return true;
		}
	}
	if (e->type() == QEvent::ContextMenu) {
		invokeSignalPopup(((QWidget*)o)->childAt(((QContextMenuEvent*)e)->pos()), ((QContextMenuEvent*)e)->globalPos());
		return true;
	}
	if (o == form && e->type() == QEvent::Destroy) {
		form = 0;
		CORE->widgetScripts->clearForm();
	}
	return QWidget::eventFilter(o, e);
}


void FormWidget::loadFile(const QString & path) {
	//qDebug() << "load" << path;
	RUNTIME->widget_list.clear();
	if (path.isEmpty()) return;
	QFile f(path);
	QFileInfo fi(f);
	if (!QFile::exists(path)) {
		if (QMessageBox::question(this, "Open file", "UI file doesn`t exists, create?", QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Cancel)
			return;
		QString fname = toCName(fi.baseName());
		if (fname.isEmpty())
			return;
		fname[0] = fname[0].toUpper();
		QFile nf(path);
		nf.open(QIODevice::ReadWrite);
		nf.write(QString(qt_new_form).arg(fname, fname, fname).toLatin1());
		nf.close();
		QTimer::singleShot(0, this, SLOT(on_actionEdit_with_Qt_Designer_triggered()));
	}
	if (!f.open(QIODevice::ReadOnly)) {
		QMessageBox::critical(this, "Open file", "Can`t open file \"" + path + "\"!");
		return;
	}
	prepareUI(path);
	QWidget * nf = ui_loader.load(&f);
	if (nf == 0) {
		QMessageBox::critical(this, "Open file", "Can`t load file \"" + path + "\"!");
		return;
	}
	RUNTIME->widget_list = ui_loader.loaded_widgets;
	foreach (QObject * o, RUNTIME->widget_list) {
		QWidget * w = qobject_cast<QWidget*>(o);
		w->setContextMenuPolicy(Qt::NoContextMenu);
		if (qobject_cast<SHSButton * >(w)) {qobject_cast<SHSButton * >(w)->buildDebug(); w->show();}
		if (qobject_cast<QTimerWidget * >(w)) qobject_cast<QTimerWidget * >(w)->buildDebug();
	}
	nf->installEventFilter(this);
	if (form != 0) delete form;
	form = mdiArea->addSubWindow(nf);
	form->setStyleSheet(QString("font: 12pt \"DejaVu Sans\";"));
	form->setWindowTitle(fi.baseName());
	form->show();
	form->installEventFilter(this);
	form->move(0, 0);
	form->adjustSize();
	CORE->widgetScripts->codeEdit->form_ui = nf;
	CORE->widgetScripts->codeEdit->updateACObjects();
	CORE->widgetScripts->codeEdit->setFocus();
}


void FormWidget::stopTest() {
	if (!tform) return;
	tform->deleteLater();
	tform = 0;
}


void FormWidget::reset() {
	RUNTIME->widget_list.clear();
	if (form)
		delete form;
	form = 0;
}


void FormWidget::editForm() {
	if (!form) return;
	QStringList args;
	args << RUNTIME->server_project.ui;
	//if (!db_path.isEmpty()) args << ("-db=" + db_path);
	QString cwd = QFileInfo(RUNTIME->server_project.ui).absoluteDir().path();
	QFile cmdf(cwd + "/_ui_config_");
	if (cmdf.open(QIODevice::ReadWrite)) {
		cmdf.resize(0);
		QTextStream ts(&cmdf);
		ts << "dat = " << RUNTIME->server_project.dat.toUtf8() << "\n";
		ts << "var = " << RUNTIME->server_project.var.toUtf8() << "\n";
		cmdf.close();
	}
	QProcess::startDetached("designer", args, cwd);
}


uint fileHash(const QString & path) {
	PIFile f(Q2PIString(path), PIIODevice::ReadOnly);
	if (!f.isOpened()) return 0;
	return f.readAll().hash();
}

QStringList replaceImages(QDir dir, QString & ui) {
	QStringList ret, regexps = SHSUiLoader::iconsRegexpList();
	QDir imgdir("res/img");
	QSet<QString> used;
	QMap<uint, QString> res_files;
	QStringList rfl = imgdir.entryList(QDir::Files);
	for (QString f: rfl) {
		QString fn = imgdir.filePath(f);
		res_files[fileHash(fn)] = fn;
	}
	for (QString a: regexps) {
		QRegularExpression re(a);
		QRegularExpressionMatch rm = re.match(ui);
		while (rm.hasMatch()) {
			QFileInfo fi(rm.captured(1));
			QString srcname = dir.filePath(rm.captured(1)), newname;
			uint srchash = fileHash(srcname);
			if (res_files.contains(srchash)) {
				newname = res_files.value(srchash);
				//qDebug() << "use by hash" << srcname << "->" << newname;
			} else {
				newname = imgdir.filePath(QString("%1_%2.%3").arg(fi.baseName(), QString::number(srchash, 16), fi.completeSuffix()));
				if (fi != QFileInfo(newname)) {
					if (QFile::exists(newname))
						QFile::remove(newname);
					QFile::copy(srcname, newname);
					res_files[fileHash(newname)] = newname;
					//qDebug() << "copy" << srcname << "->" << newname;
				}
			}
			used << newname;
			ui.replace(rm.capturedStart(1), rm.capturedLength(1), dir.relativeFilePath(newname));
			rm = re.match(ui, rm.capturedStart() + (a.size() / 2));
		}
	}
	/// TODO: maybe later, by button
	/*rfl = imgdir.entryList(QDir::Files);
	for (QString f: rfl) {
		QString fn = imgdir.filePath(f);
		if (!used.contains(fn)) {
			//qDebug() << "remove unused" << fn;
			QFile::remove(fn);
		}
	}*/
	return ret;
}

void FormWidget::prepareUI(const QString & path) {
	QFile f(path);
	f.open(QIODevice::ReadWrite);
	QString ui = QString::fromUtf8(f.readAll());
	if (ui.size() <= 4) return;

	QDir("res").mkdir("img");
	replaceImages(QFileInfo(path).absoluteDir(), ui);

	f.seek(0);
	f.resize(0);
	f.write(ui.toUtf8());
}


QMenu * FormWidget::widgetPopupMenu(QWidget * w) {
	QMenu * m = new QMenu();
	connect(m, SIGNAL(aboutToHide()), m, SLOT(deleteLater()));
	auto addLabel = [m](QString text){
		QWidgetAction * ta = new QWidgetAction(m);
		QLabel * lbl = new QLabel(text);
		lbl->setFrameShadow(QFrame::Sunken);
		lbl->setFrameShape(QFrame::StyledPanel);
		lbl->setStyleSheet("font: bold;");
		lbl->setAlignment(Qt::AlignCenter);
		ta->setDefaultWidget(lbl);
		m->addAction(ta);
	};
	addLabel(QString("%1\n%2").arg(w->objectName(), w->metaObject()->className()));
	auto * a = m->addAction("Copy name");
	connect(a, &QAction::triggered, this, [w](){QApplication::clipboard()->setText(w->objectName());});
	a = m->addAction("Copy classname");
	connect(a, &QAction::triggered, this, [w](){QApplication::clipboard()->setText(w->metaObject()->className());});
	addLabel(QString("Select signal"));
	const QMetaObject * mo = w->metaObject();
	QSet<QString> in_list;
	for (int i = 0; i < mo->methodCount(); ++i) {
		QMetaMethod mm = mo->method(i);
		if (mm.methodType() == QMetaMethod::Signal) {
			if (in_list.contains(mm.name())) continue;
			in_list << mm.name();
			m->addAction(QString("%1").arg(QString(mm.methodSignature())),
						 this, SLOT(addSignal()))->setData(QVariant::fromValue<QMetaMethod>(mm));
		}
	}
	return m;
}


void FormWidget::invokeSignalPopup(QWidget * w, QPoint global_pos) {
	if (!w) return;
	while (!RUNTIME->widget_list.contains(w) && w)
		w = w->parentWidget();
	if (w) if (w->objectName().startsWith("qt_")) w = w->parentWidget();
	if (w) {
		QWidget * nw = w;
		while (nw != 0) {
			if (qobject_cast<SHSSlider * >(nw)) {
				w = nw;
				break;
			}
			nw = nw->parentWidget();
		}
	}
	//qDebug() << w;
	QMenu * m = widgetPopupMenu(w);
	signal_src = w->objectName();
	m->popup(global_pos);
}


void FormWidget::addSignal() {
	QAction * a = qobject_cast<QAction * >(sender());
	if (a == 0) return;
	CORE->widgetScripts->addSignal(a->data().value<QMetaMethod>(), signal_src);
}
