/*
    QGL RendererMaterial
    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/>.
*/

#define GL_GLEXT_PROTOTYPES
#include "renderer_material.h"

#include "glmesh.h"
#include "gltexture_manager.h"
#include "qglview.h"
#include "renderer.h"

#include <QOpenGLExtraFunctions>
#include <qad_types.h>

using namespace QGLEngineShaders;


RendererMaterial::RendererMaterial(Renderer * r_): r(r_), fbo_mat_thumb(r->view, 7, true, GL_RGBA16F) {
	mat_sphere = Primitive::ellipsoid(16, 16);
	mat_camera = new Camera();
	mat_camera->setPos(QVector3D(2, 2, 2));
	mat_camera->setAim(QVector3D());
	mat_camera->setFOV(45.);
	mat_light = new Light();
	mat_light->setPos(QVector3D(50, 100, 25));
	mat_light->setCastShadows(false);
	last_thumb_material = 0;
}


RendererMaterial::~RendererMaterial() {
	delete mat_sphere;
	delete mat_camera;
	delete mat_light;
}


void RendererMaterial::init(int width, int height) {
	fbo_mat_thumb.reinit();
	mat_sphere->reinit();
	resize(width, height);
}


void RendererMaterial::resize(int width, int height) {
	fbo_mat_thumb.enablePixelBuffer();
	fbo_mat_thumb.resize(256, 256);
}


void RendererMaterial::renderMaterial(Material * m) {
	// qDebug() << "renderMaterial" << m;
	last_thumb_material         = m;
	QOpenGLShaderProgram * prog = 0;
	QOpenGLExtraFunctions * f   = r->view;
	fbo_mat_thumb.bind();
	glEnableDepth();
	glClearFramebuffer(QColor(0, 0, 0, 0));
	auto role = m->hasTransparency() ? Renderer::srGeometryTransparentPass : Renderer::srGeometrySolidPass;
	if (r->bindShader(role, &prog)) {
		r->setUniformMaps(prog);
		r->setUniformCamera(prog, mat_camera, true, fbo_mat_thumb.size());
		prog->setUniformValue("out_index_normal", Renderer::dbrNormalZ);
		prog->setUniformValue("out_index_metal", Renderer::dbrMetalRoughReflectFlags);
		// qDebug() << mat_camera->viewMatrix();
		r->textures_empty.bind(f, tarEmpty);
		r->textures_maps.bind(f, tarMaps);
		Object o;
		o.material       = m->_index;
		o.f_accept_light = 1;
		mat_sphere->loadObject(f, o);
		mat_sphere->draw(f, 1);
	}
	fbo_mat_thumb.bindColorTextures({Renderer::dbrDiffuse,
									 Renderer::dbrNormalZ,
									 Renderer::dbrMetalRoughReflectFlags,
									 Renderer::dbrEmission,
									 Renderer::dbrSpeedBitangXY,
									 Renderer::dbrGeoNormal});
	fbo_mat_thumb.setWriteBuffer(6);
	if (r->bindShader(Renderer::srLightOmniPass, &prog)) {
		r->setUniformCamera(prog, mat_camera, true, fbo_mat_thumb.size());
		r->setUniformViewCorners(prog, mat_camera, fbo_mat_thumb.size());
		for (int i = 0; i <= 5; ++i)
			prog->setUniformValue(QString("tex_%1").arg(i).toLatin1().constData(), i);
		prog->setUniformValue("lights_start", 0);
		prog->setUniformValue("lights_count", 1);
		QMap<int, QList<Light *>> mat_l;
		mat_l[Light::Omni] << mat_light;
		r->reloadLightsParameters(mat_l);
		r->reloadLightsPositions(mat_camera);
		glClearFramebuffer(Qt::black, false);
		r->renderQuad(prog, r->quad, mat_camera);
		r->view->scene()->setLightsChanged();
	}
	fbo_mat_thumb.queryImage(6);
	fbo_mat_thumb.release();
}


void RendererMaterial::procQueue() {
	if (last_thumb_material) {
		mat_thumbnails[last_thumb_material] = fbo_mat_thumb.getImage();
		emit r->view->materialThumbnailCreated(last_thumb_material);
		last_thumb_material = 0;
	}
	if (!mat_thumb_queue.isEmpty()) renderMaterial(mat_thumb_queue.dequeue());
}


QImage RendererMaterial::materialThumbnail(Material * m) {
	return mat_thumbnails.value(m);
}


void RendererMaterial::recreateMaterialThumbnails(bool force_all) {
	if (force_all) {
		mat_thumb_queue.clear();
		// qDebug() << "recreateMaterialThumbnails" << view->scene_->materials;
		foreach(Material * m, r->view->scene()->materials)
			mat_thumb_queue.enqueue(m);
	} else {
		foreach(Material * m, r->view->scene()->changed_materials)
			if (!mat_thumb_queue.contains(m)) mat_thumb_queue.enqueue(m);
	}
}
