/*
    QGL ObjectEditor
    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 "object_editor.h"

#include "qglview.h"
#include "ui_object_editor.h"

#include <QScrollBar>
#include <scroll_spin_box.h>
#include <spinslider.h>


ObjectEditor::ObjectEditor(QWidget * parent): QWidget(parent) {
	ui = new Ui::ObjectEditor();
	ui->setupUi(this);
	ui->scrollArea->viewport()->setAutoFillBackground(false);
	ui->scrollAreaWidgetContents->setAutoFillBackground(false);
	ui->mapLight->configure(tr("Map"), false);
	view        = 0;
	active      = true;
	ignore_next = false;
	on_comboLightType_currentIndexChanged(0);
	ui->scrollArea->setEnabled(false);
	ui->labelAimDist->hide();
	ui->spinAimDist->hide();
	ui->mapLight->hide();

	QObjectList ol;
	ol << ui->spinPosX << ui->spinPosY << ui->spinPosZ << ui->spinRotationX << ui->spinRotationY << ui->spinRotationZ << ui->spinScaleX
	   << ui->spinScaleY << ui->spinScaleZ;
	foreach(QObject * o, ol)
		connect(o, SIGNAL(valueChanged(double)), this, SLOT(spinChanged(double)));

	ol.clear();
	ol << ui->spinRotationTexture << ui->spinOffsetTextureX << ui->spinOffsetTextureY << ui->spinScaleTextureX << ui->spinScaleTextureY;
	foreach(QObject * o, ol)
		connect(o, SIGNAL(valueChanged(double)), this, SLOT(spinTextureChanged(double)));

	ol.clear();
	ol << ui->spinLightIntensity << ui->spinLightDecayConst << ui->spinLightDecayLinear << ui->spinLightDecayQuadratic
	   << ui->spinLightAngleStart << ui->spinLightAngleEnd << ui->spinAimDist << ui->spinLightSize;
	foreach(QObject * o, ol)
		connect(o, SIGNAL(valueChanged(double)), this, SLOT(spinLightChanged(double)));

	ol.clear();
	ol << ui->spinCameraFOV << ui->spinCameraDepthStart << ui->spinCameraRoll << ui->spinAimDist;
	foreach(QObject * o, ol)
		connect(o, SIGNAL(valueChanged(double)), this, SLOT(spinCameraChanged(double)));

	ol.clear();
	ol << ui->checkVisible << ui->checkCastShadows << ui->checkReceiveShadows << ui->checkAcceptLight << ui->checkAcceptFog
	   << ui->checkCameraMirrorX << ui->checkCameraMirrorY;
	foreach(QObject * o, ol)
		connect(o, SIGNAL(toggled(bool)), this, SLOT(checkChanged(bool)));
}


void ObjectEditor::assignQGLView(QGLView * v) {
	view = v;
	connect(view, SIGNAL(selectionChanged()), this, SLOT(selectionChanged()));
	connect(view, SIGNAL(objectsPositionChanged()), this, SLOT(selectionChanged()));
	connect(view->scene(), SIGNAL(treeChanged()), this, SLOT(selectionChanged()));
	selectionChanged();
}


void ObjectEditor::changeEvent(QEvent * e) {
	QWidget::changeEvent(e);
	switch (e->type()) {
	case QEvent::LanguageChange: ui->retranslateUi(this); break;
	default: break;
	}
}


void ObjectEditor::selectionChanged() {
	int vpos = ui->scrollArea->verticalScrollBar()->value();
	if (ignore_next) {
		ignore_next = false;
		return;
	}
	if (!view) {
		ui->scrollArea->setEnabled(false);
		return;
	}
	ObjectBaseList sol = view->selectedObjects(true);
	if (sol.isEmpty()) {
		ui->labelTitle->setText(tr("[No selected]"));
		ui->scrollArea->setEnabled(false);
		return;
	}
	ui->scrollArea->setEnabled(true);
	if (prev_sol == sol) return;
	prev_sol = sol;
	if (sol.size() == 1) {
		setObject(sol[0]);
		ui->scrollArea->verticalScrollBar()->setValue(vpos);
		return;
	}
	bool hl = !view->selectedLights().isEmpty(), hc = !view->selectedCameras().isEmpty();
	ui->groupLight->setVisible(hl);
	ui->groupCamera->setVisible(hc);
	ui->labelAimDist->setVisible(hl || hc);
	ui->spinAimDist->setVisible(hl || hc);
	ui->labelTitle->setText(tr("[%1 objects]").arg(sol.size()));
}


void ObjectEditor::setObject(ObjectBase * o) {
	active = false;
	ui->labelTitle->setText(o->name());
	ui->spinPosX->setValue(o->posX());
	ui->spinPosY->setValue(o->posY());
	ui->spinPosZ->setValue(o->posZ());
	ui->spinRotationX->setValue(o->rotationX());
	ui->spinRotationY->setValue(o->rotationY());
	ui->spinRotationZ->setValue(o->rotationZ());
	ui->spinScaleX->setValue(o->scaleX());
	ui->spinScaleY->setValue(o->scaleY());
	ui->spinScaleZ->setValue(o->scaleZ());

	ui->spinRotationTexture->setValue(o->textureTransform().rotationZ());
	ui->spinOffsetTextureX->setValue(o->textureTransform().translation().x());
	ui->spinOffsetTextureY->setValue(o->textureTransform().translation().y());
	ui->spinScaleTextureX->setValue(o->textureTransform().scale3D().x());
	ui->spinScaleTextureY->setValue(o->textureTransform().scale3D().y());

	ui->checkVisible->setChecked(o->isVisible());
	ui->checkAcceptLight->setChecked(o->isAcceptLight());
	ui->checkAcceptFog->setChecked(o->isAcceptFog());
	ui->checkCastShadows->setChecked(o->isCastShadows());
	ui->checkReceiveShadows->setChecked(o->isReceiveShadows());
	ui->buttonColor->setColor(o->color());
	bool is_l = o->type() == ObjectBase::glLight;
	bool is_c = o->type() == ObjectBase::glCamera;
	ui->labelAimDist->setVisible(is_l || is_c);
	ui->spinAimDist->setVisible(is_l || is_c);
	ui->groupLight->setVisible(is_l);
	ui->groupCamera->setVisible(is_c);
	if (is_l) {
		Light * l = globject_cast<Light *>(o);
		// bool is_dir = l->light_type == Light::Directional, is_cone = l->light_type == Light::Cone;
		ui->comboLightType->setCurrentIndex(l->light_type);
		ui->spinLightIntensity->setValue(l->intensity);
		ui->spinLightDecayConst->setValue(l->decay_const);
		ui->spinLightDecayLinear->setValue(l->decay_linear);
		ui->spinLightDecayQuadratic->setValue(l->decay_quadratic);
		ui->spinLightAngleStart->setValue(l->angle_start);
		ui->spinLightAngleEnd->setValue(l->angle_end);
		ui->spinLightSize->setValue(l->size);
		ui->spinAimDist->setValue(l->distance());
		on_comboLightType_currentIndexChanged(ui->comboLightType->currentIndex());
		if (l->light_type == Light::Cone) ui->mapLight->setMap(&(l->light_map));
	}
	if (is_c) {
		Camera * c = globject_cast<Camera *>(o);
		ui->checkCameraMirrorX->setChecked(c->isMirrorX());
		ui->checkCameraMirrorY->setChecked(c->isMirrorY());
		ui->spinCameraFOV->setValue(c->FOV());
		ui->spinCameraRoll->setValue(c->angleRoll());
		ui->spinCameraDepthStart->setValue(c->depthStart());
		ui->spinAimDist->setValue(c->distance());
	}
	active = true;
}


void ObjectEditor::on_spinLightAngleStart_valueChanged(double v) {
	if (ui->spinLightAngleEnd->value() < v) ui->spinLightAngleEnd->setValue(v);
}


void ObjectEditor::on_spinLightAngleEnd_valueChanged(double v) {
	if (ui->spinLightAngleStart->value() > v) ui->spinLightAngleStart->setValue(v);
}


void ObjectEditor::on_comboLightType_currentIndexChanged(int index) {
	bool ang = (index == Light::Cone);
	ui->labelLightAngle->setVisible(ang);
	ui->labelLightAngleDash->setVisible(ang);
	ui->spinLightAngleStart->setVisible(ang);
	ui->spinLightAngleEnd->setVisible(ang);
	ui->mapLight->setVisible(ang);
	if (!view || !active) return;
	QList<Light *> sll = view->selectedLights();
	foreach(Light * o, sll) {
		o->light_type = (Light::Type)index;
		o->apply();
	}
}


void ObjectEditor::on_buttonColor_colorChanged(const QColor & v) {
	if (!view || !active) return;
	ObjectBaseList sol = view->selectedObjects();
	foreach(ObjectBase * o, sol)
		o->setColor(v);
	QList<Light *> sll = view->selectedLights();
	foreach(Light * o, sll)
		o->apply();
}


void ObjectEditor::on_mapLight_changed() {
	if (!view || !active) return;
	QList<Light *> sll = view->selectedLights();
	foreach(Light * o, sll) {
		o->apply();
	}
}


void ObjectEditor::spinChanged(double v) {
	if (!view || !active) return;
	ObjectBaseList sol = view->selectedObjects(true);
	QObject * s        = sender();
	foreach(ObjectBase * o, sol) {
		if (s == ui->spinPosX) o->setPosX(v);
		if (s == ui->spinPosY) o->setPosY(v);
		if (s == ui->spinPosZ) o->setPosZ(v);
		if (s == ui->spinRotationX) o->setRotationX(v);
		if (s == ui->spinRotationY) o->setRotationY(v);
		if (s == ui->spinRotationZ) o->setRotationZ(v);
		if (s == ui->spinScaleX) o->setScaleX(v);
		if (s == ui->spinScaleY) o->setScaleY(v);
		if (s == ui->spinScaleZ) o->setScaleZ(v);
	}
	ignore_next = true;
}


void ObjectEditor::spinTextureChanged(double v) {
	if (!view || !active) return;
	ObjectBaseList sol = view->selectedObjects(true);
	QObject * s        = sender();
	foreach(ObjectBase * o, sol) {
		auto ttr = o->textureTransform();
		if (s == ui->spinRotationTexture) ttr.setRotationZ(v);
		if (s == ui->spinOffsetTextureX) ttr.setTranslationX(v);
		if (s == ui->spinOffsetTextureY) ttr.setTranslationY(v);
		if (s == ui->spinScaleTextureX) ttr.setScaleX(v);
		if (s == ui->spinScaleTextureY) ttr.setScaleY(v);
		o->setTextureTransform(ttr);
	}
	ignore_next = true;
}


void ObjectEditor::spinLightChanged(double v) {
	if (!view || !active) return;
	QList<Light *> sll = view->selectedLights();
	QObject * s        = sender();
	foreach(Light * o, sll) {
		if (s == ui->spinLightIntensity) o->intensity = v;
		if (s == ui->spinLightDecayConst) o->decay_const = v;
		if (s == ui->spinLightDecayLinear) o->decay_linear = v;
		if (s == ui->spinLightDecayQuadratic) o->decay_quadratic = v;
		if (s == ui->spinLightAngleStart) o->angle_start = v;
		if (s == ui->spinLightAngleEnd) o->angle_end = v;
		if (s == ui->spinAimDist) o->setDistance(v);
		if (s == ui->spinLightSize) o->size = v;
		o->apply();
	}
	ignore_next = true;
}


void ObjectEditor::spinCameraChanged(double v) {
	if (!view || !active) return;
	QList<Camera *> scl = view->selectedCameras();
	QObject * s         = sender();
	foreach(Camera * o, scl) {
		if (s == ui->spinCameraFOV) o->setFOV(v);
		if (s == ui->spinCameraRoll) o->setAngleRoll(v);
		if (s == ui->spinCameraDepthStart) o->setDepthStart(v);
		if (s == ui->spinAimDist) o->setDistance(v);
	}
	ignore_next = true;
}


void ObjectEditor::checkChanged(bool v) {
	if (!view || !active) return;
	ObjectBaseList sol  = view->selectedObjects();
	QList<Camera *> scl = view->selectedCameras();
	QObject * s         = sender();
	foreach(ObjectBase * o, sol) {
		if (s == ui->checkVisible) o->setVisible(v);
		if (s == ui->checkCastShadows) o->setCastShadows(v);
		if (s == ui->checkReceiveShadows) o->setReceiveShadows(v);
		if (s == ui->checkAcceptLight) o->setAcceptLight(v);
		if (s == ui->checkAcceptFog) o->setAcceptFog(v);
	}
	foreach(Camera * o, scl) {
		if (s == ui->checkCameraMirrorX) o->setMirrorX(v);
		if (s == ui->checkCameraMirrorY) o->setMirrorY(v);
	}
}
