/*
    Stanley Designer
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "qglview_window.h"

#include "loader_3ds.h"
#include "loader_ase.h"
#include "loader_dae.h"
#include "loader_obj.h"
#include "loader_qgl.h"
#include "qad_types.h"

#include <QGraphicsRectItem>
#include <QImageReader>
#include <QMessageBox>


QGLViewWindow::QGLViewWindow(QWidget * parent): QMainWindow(parent), Ui::QGLViewWindow() {
	setupUi(this);
	session.setFile("session_qglview_test.conf");
	session.addEntry(this);
	icon_geo    = QIcon(":/icons/type-geo.png");
	icon_camera = QIcon(":/icons/type-camera.png");
	icon_light  = QIcon(":/icons/type-light.png");
	QAction * a = new QAction(QIcon(":/icons/edit-delete.png"), "Remove");
	connect(a, SIGNAL(triggered()), this, SLOT(removeObjects()));
	treeObjects->addAction(a);
	spinViewLineWidth->setValue(lineThickness() * 2);

	sel_obj = nullptr;
	view->view()->camera()->setAim(QVector3D());
	view->view()->camera()->setPos(QVector3D(2, 2, 2));
	//	view->setFrameShape(QFrame::NoFrame);
	view->view()->setRenderer(new RendererSimple(view->view()));
	view->view()->setMouseRotateEnabled(true);
	view->view()->setMouseSelectionEnabled(true);
	view->view()->setSelectionHaloEnabled(true);
	view->view()->setHoverHaloEnabled(true);
	view->view()->setHoverHaloFillAlpha(0.);
	view->view()->setSelectionHaloFillAlpha(0.);
	view->view()->setBackColor(Qt::lightGray);
	view->view()->setDepthStart(0.1);
	view->view()->setDepthEnd(100000.);

	spinFOV->setValue(view->view()->FOV());
	spinDepthStart->setValue(view->view()->depthStart());
	spinDepthEnd->setValue(view->view()->depthEnd());
	groupHoverHalo->setChecked(view->view()->isHoverHaloEnabled());
	groupSelectionHalo->setChecked(view->view()->isSelectionHaloEnabled());
	spinHoverHaloFill->setValue(view->view()->hoverHaloFillAlpha());
	spinSelectionHaloFill->setValue(view->view()->selectionHaloFillAlpha());
	colorHoverHalo->setColor(view->view()->hoverHaloColor());
	colorSelectionHalo->setColor(view->view()->selectionHaloColor());
	checkFXAA->setChecked(view->view()->isFeatureEnabled(QGLView::qglFXAA));
	checkMSAA->setChecked(view->view()->isFeatureEnabled(QGLView::qglMSAA));
	colorBack->setColor(view->view()->backColor());
	colorAmbient->setColor(view->view()->ambientColor());
	checkCameraOrbit->setChecked(view->view()->isCameraOrbit());

	groupShadows->setChecked(view->view()->isFeatureEnabled(QGLView::qglShadowsEnabled));
	groupEyeAccomodation->setChecked(view->view()->isFeatureEnabled(QGLView::qglEyeAccomodationEnabled));
	groupBloom->setChecked(view->view()->isFeatureEnabled(QGLView::qglBloomEnabled));
	groupMotionBlur->setChecked(view->view()->isFeatureEnabled(QGLView::qglMotionBlurEnabled));
	groupReflections->setChecked(view->view()->isFeatureEnabled(QGLView::qglReflectionsEnabled));
	checkSoftShadows->setChecked(view->view()->isFeatureEnabled(QGLView::qglShadowsSoftEnabled));
	groupSSAO->setChecked(view->view()->isFeatureEnabled(QGLView::qglSSAOEnabled));
	spinAccom->setValue(view->view()->feature(QGLView::qglEyeAccomodationTime).toDouble());
	spinAccomMS->setValue(view->view()->feature(QGLView::qglEyeAccomodationMaxSpeed).toDouble());
	checkReflectionsBlur->setChecked(view->view()->isFeatureEnabled(QGLView::qglReflectionsBlur));
	spinShadowmapSize->setValue(view->view()->feature(QGLView::qglShadowsMapSize).toInt());
	spinMotionBlurFactor->setValue(view->view()->feature(QGLView::qglMotionBlurFactor).toDouble());
	spinMotionBlurSteps->setValue(view->view()->feature(QGLView::qglMotionBlurSteps).toInt());
	spinBloomFactor->setValue(view->view()->feature(QGLView::qglBloomFactor).toDouble());
	spinBloomRadius->setValue(view->view()->feature(QGLView::qglBloomRadius).toInt());
	spinBloomThreshold->setValue(view->view()->feature(QGLView::qglBloomThreshold).toDouble());
	spinSSAORadius->setValue(view->view()->feature(QGLView::qglSSAORadius).toInt());
	groupDOF->setChecked(view->view()->isFeatureEnabled(QGLView::qglDepthOfFieldEnabled));
	checkDOFAutoFocus->setChecked(view->view()->isFeatureEnabled(QGLView::qglDepthOfFieldAutoFocusEnabled));
	spinDOFFocus->setValue(view->view()->feature(QGLView::qglDepthOfFieldFocus).toDouble());
	spinDOFDiaphragm->setValue(view->view()->feature(QGLView::qglDepthOfFieldDiaphragm).toDouble());
	spinDOFSpeed->setValue(view->view()->feature(QGLView::qglDepthOfFieldAutoFocusSpeed).toDouble());

	axis = new GLObjectBase();
	GLObjectBase * obj;
	float al                      = 1.;
	obj                           = new GLPrimitiveLine(QVector3D(0, 0, -al), QVector3D(0, 0, al));
	obj->material().color_diffuse = Qt::darkBlue;
	obj->setAcceptLight(false);
	axis->addChild(obj);
	obj                           = new GLPrimitiveLine(QVector3D(-al, 0, 0), QVector3D(al, 0, 0));
	obj->material().color_diffuse = Qt::darkRed;
	obj->setAcceptLight(false);
	axis->addChild(obj);
	obj                           = new GLPrimitiveLine(QVector3D(0, -al, 0), QVector3D(0, al, 0));
	obj->material().color_diffuse = Qt::darkGreen;
	obj->setAcceptLight(false);
	axis->addChild(obj);
	view->view()->addObject(axis);

	cam_light            = new Light();
	cam_light->intensity = 0.5;
	cam_light->setName("Camera_Light");
	view->view()->camera()->addChild(cam_light);
	view->view()->start(-1);
	startTimer(1000 / 60);

	connect(view->view(),
	        SIGNAL(selectionChanged(GLObjectBase *, GLObjectBase *)),
	        this,
	        SLOT(selectionChanged(GLObjectBase *, GLObjectBase *)));
	connect(view->view(), SIGNAL(keyEvent(Qt::Key, Qt::KeyboardModifiers)), this, SLOT(view_keyEvent(Qt::Key, Qt::KeyboardModifiers)));
	connect(matEditor, SIGNAL(changed()), this, SLOT(materialChanged()));

	// view->view()->addObject(&partsys);
	partsys.material().color_diffuse = Qt::red;
	treeProps->assignObject(&partsys);
	session.load();
}


QGLViewWindow::~QGLViewWindow() {
	session.save();
	// delete ps;
}


void QGLViewWindow::changeEvent(QEvent * e) {
	QMainWindow::changeEvent(e);
	if (e->type() == QEvent::LanguageChange) {
		retranslateUi(this);
		return;
	}
}


void QGLViewWindow::timerEvent(QTimerEvent *) {
	// static double t = 0.;
	// cam_light->intensity = checkCameraLight->isChecked() ? 0.5 : 0.;
	cam_light->setVisible(checkCameraLight->isChecked());
	//((RendererSimple*)(view->view()->renderer()))->mpos = view->view()->mapFromGlobal(QCursor::pos());
	statusBar()->showMessage(QString("FPS: %1").arg(QString::number(view->view()->currentFPS(), 'f', 2)));
}


void QGLViewWindow::loadFile(const QString & path) {
	prev_path = path;
	importFile(path);
}


void QGLViewWindow::importFile(const QString & path) {
	QApplication::setOverrideCursor(Qt::WaitCursor);
	QFileInfo fi(path);
	GLObjectBase * o = nullptr;
	if (fi.suffix().toLower() == "qgl") o = loadFromQGLFile(path);
	if (fi.suffix().toLower() == "ase") o = loadFromASEFile(path);
	if (fi.suffix().toLower() == "3ds") o = loadFrom3DSFile(path);
	if (fi.suffix().toLower() == "obj") o = loadFromOBJFile(path);
	if (fi.suffix().toLower() == "dae") o = loadFromDAEFile(path);
	QApplication::restoreOverrideCursor();
	if (!o) {
		QMessageBox::critical(this, "Import", "Can`t load " + path + "!");
		return;
	}
	o->setName(fi.baseName());
	view->view()->addObject(o);
	objectsTreeChanged();
}


void QGLViewWindow::makeObjetTree(const GLObjectBase * o, QTreeWidgetItem * ti) {
	if (o == axis) return;
	for (int i = 0; i < o->childCount(); ++i) {
		const GLObjectBase * co = o->child(i);
		QTreeWidgetItem * ci    = new QTreeWidgetItem(ti);
		ci->setText(0, co->name());
		ci->setData(0, Qt::UserRole, quintptr(co));
		switch (co->type()) {
		case GLObjectBase::glMesh: ci->setIcon(0, icon_geo); break;
		case GLObjectBase::glLight: ci->setIcon(0, icon_light); break;
		case GLObjectBase::glCamera: ci->setIcon(0, icon_camera); break;
		default: break;
		}
		makeObjetTree(co, ci);
	}
}


void QGLViewWindow::selectionChanged(GLObjectBase * cur, GLObjectBase *) {
	sel_obj = cur;
	// qDebug() << "selected" << (cur != 0 ? cur->name() : "0");
	labelName->setText(cur != nullptr ? cur->name() : "");
	/**if (cur == 0) box->hide();
	else {
	    box->setScale(cur->boundingBox().size());
	    box->setPos(cur->boundingBox().pos());
	    Box3D b = cur->boundingBox().movedTo(-cur->boundingBox().center());
	    b.z = -b.z - b.height;
	    ps->setEmitterRect(b);
	    cur->addChild(box);
	    box->show();
	}*/
	objectEditor->setObject(sel_obj);
	if (sel_obj == nullptr) return;
	matEditor->setMaterial(sel_obj->material());
	// qDebug() << sel_obj->boundingBox();
}


void QGLViewWindow::materialChanged() {
	if (sel_obj == nullptr) return;
	sel_obj->setMaterial(matEditor->material());
}


void QGLViewWindow::on_comboRenderer_currentIndexChanged(int val) {
	GLRendererBase * pr = nullptr;
	switch (val) {
	case 0: view->view()->setRenderer(new RendererSimple(view->view()), &pr); break;
	case 1:
		view->view()->setRenderer(new RendererDeferredShading(view->view()), &pr);
		break;
		// case 2: view->view()->setRenderer(new RendererRT(view), &pr); break;
	}
	if (pr != nullptr) delete pr;
}


void QGLViewWindow::on_actionReset_triggered() {
	view->view()->removeObject(axis, false);
	view->view()->clearObjects(true);
	view->view()->addObject(axis);
	objectsTreeChanged();
}


void QGLViewWindow::on_actionImport_triggered() {
	QStringList fl = QFileDialog::getOpenFileNames(this,
	                                               "Select files",
	                                               prev_path,
	                                               "Supported types(*.qgl *.ase *.3ds *.obj *.dae);;"
	                                               "QGLView(*.qgl);;"
	                                               "Ascii Scene Export(*.ase);;"
	                                               "3D Studio(*.3ds);;"
	                                               "Wavefront OBJ(*.obj);;"
	                                               "Collada(*.dae)");
	if (fl.isEmpty()) return;
	prev_path = fl.back();
	foreach(QString f, fl)
		importFile(f);
}


void QGLViewWindow::on_actionSave_triggered() {
	QString f = QFileDialog::getSaveFileName(this, "Select file", prev_path, "QGLView(*.qgl)");
	if (f.isEmpty()) return;
	if (f.right(4).toLower() != ".qgl") f += ".qgl";
	prev_path = f;
	view->view()->removeObject(axis);
	QApplication::setOverrideCursor(Qt::WaitCursor);
	saveToQGLFile(f, &(view->view()->rootObject()));
	QApplication::restoreOverrideCursor();
	view->view()->addObject(axis);
}


void QGLViewWindow::on_actionSaveSelected_triggered() {
	if (!sel_obj) return;
	QString f = QFileDialog::getSaveFileName(this, "Select file", prev_path, "QGLView(*.qgl)");
	if (f.isEmpty()) return;
	if (f.right(4).toLower() != ".qgl") f += ".qgl";
	prev_path = f;
	QApplication::setOverrideCursor(Qt::WaitCursor);
	saveToQGLFile(f, sel_obj);
	QApplication::restoreOverrideCursor();
}


void QGLViewWindow::on_actionOpen_triggered() {
	QString f = QFileDialog::getOpenFileName(this,
	                                         "Select file",
	                                         prev_path,
	                                         "Supported types(*.qgl *.ase *.3ds *.obj *.dae);;"
	                                         "QGLView(*.qgl);;"
	                                         "Ascii Scene Export(*.ase);;"
	                                         "3D Studio(*.3ds);;"
	                                         "Wavefront OBJ(*.obj);;"
	                                         "Collada(*.dae)");
	if (f.isEmpty()) return;
	prev_path = f;
	importFile(f);
}


void QGLViewWindow::view_keyEvent(Qt::Key k, Qt::KeyboardModifiers m) {
	// qDebug() << k;
	double spd = 0.2;
	if (m.testFlag(Qt::ShiftModifier)) spd = 0.5;
	switch (k) {
	case Qt::Key_W: view->view()->camera()->moveForward(spd); break;
	case Qt::Key_S: view->view()->camera()->moveBackward(spd); break;
	case Qt::Key_A: view->view()->camera()->moveLeft(spd); break;
	case Qt::Key_D: view->view()->camera()->moveRight(spd); break;
	default: break;
	}
}


void QGLViewWindow::on_treeObjects_itemClicked(QTreeWidgetItem * ti, int) {
	((GLObjectBase *)(ti->data(0, Qt::UserRole).toULongLong()))->select();
	// qDebug() << ((GLObjectBase*)(ti->data(0, Qt::UserRole).toULongLong()))->type();
	if (sel_obj->type() == GLObjectBase::glCamera) view->view()->setCamera((Camera *)sel_obj);
}


void QGLViewWindow::removeObjects() {
	QList<QTreeWidgetItem *> sil = treeObjects->selectedItems();
	foreach(QTreeWidgetItem * i, sil) {
		GLObjectBase * o = (GLObjectBase *)(i->data(0, Qt::UserRole).toULongLong());
		delete o;
	}
	qDeleteAll(sil);
}


void QGLViewWindow::objectsTreeChanged() {
	treeObjects->clear();
	makeObjetTree(&(view->view()->rootObject()), treeObjects->invisibleRootItem());
	treeObjects->expandAll();
}


void QGLViewWindow::on_pushButton_clicked() {
	// view->view()->removeLight(view->view()->lightsCount() - 1);
	// setWindowTitle(QString::number(view->view()->lightsCount()));
	QVector3D wp = view->view()->light(0)->worldPos();
	view->view()->camera()->setPos(wp);
	view->view()->camera()->setAim(
		wp + (view->view()->light(0)->worldTransform() * QVector4D(view->view()->light(0)->direction)).toVector3D() * 100);
}


void QGLViewWindow::on_pushButton_3_clicked() {
	QList<GLObjectBase *> ol = view->view()->objects(true);
	qDebug() << ol.size();
	foreach(GLObjectBase * i, ol) {
		i->VBO().rebuffer();
	}
}
