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

#ifndef RENDERER_H
#define RENDERER_H

#include "glcubemap.h"
#include "glframebuffereffectbase.h"
#include "renderer_base.h"
#include "renderer_material.h"
#include "renderer_selection.h"
#include "renderer_service.h"
#include "tonemapping_proc.h"

#include <QQueue>


class QGLENGINE_CORE_EXPORT Renderer: public RendererBase {
	friend class QGLView;
	friend class MouseController;
	friend class RendererMaterial;
	friend class RendererService;
	friend class RendererSelection;
	friend class FramebufferEffectBase;
	friend class TonemappingProc;
	enum ShaderRole {
		// Selection
		srSelectionFill,
		srSelectionHalo,
		srSelectionApply,
		srSelectionFrame,

		// Service
		srServiceFill,
		srServiceFrame,
		srServiceLine,

		// Deferred shading
		srGeometrySolidPass,
		srGeometryTransparentPass,
		srLightOmniPass,
		srLightSpotPass,
		srFinalPass,
		srTonemapPass,
		srShadowConePass,
		srShadowOmniPass,
	};
	enum OutBufferRole {
		obrSolidOmni,
		obrSolidSpot,
		obrTransparentOmni,
		obrTransparentSpot,
		obrLighting,

		obrGeneral0,
		obrGeneral1,
		obrGeneral2,
		obrGeneral3,

		obrBuffersCount,
	};

public:
	Renderer(QGLView * view_);
	virtual ~Renderer();

	enum DeferredBufferRole {
		dbrDiffuse,
		dbrNormalZ,
		dbrMetalRoughReflectFlags,
		dbrEmission,
		dbrSpeedBitangXY,
		dbrGeoNormal,

		dbrNormalZSolid,
		dbrMetalRoughReflectFlagsSolid,

		dbrBuffersCount,
	};

	void init(int width, int height);
	void resize(int width, int height);
	void reloadShaders();
	void renderScene();
	void setCameraLightMode(int m);
	int cameraLightMode() const { return camera_light_mode; }
	void setGrabImage(bool on);
	bool isGrabImage() const { return is_grabbing; }
	QImage getImage() const { return last_img; }
	void addFramebufferEffect(FramebufferEffectBase * e);
	void clearFramebufferEffects() { fb_effects.clear(); }
	QVector<FramebufferEffectBase *> framebufferEffects() const { return fb_effects; }
	QString getTimings() const { return timings; }

	QImage materialThumbnail(Material * m) { return rend_mat.materialThumbnail(m); }
	void recreateMaterialThumbnails(bool force_all = false) { rend_mat.recreateMaterialThumbnails(force_all); }

	void setSadowsEnabled(bool on);
	bool isShadowsEnabled() const { return shadows_enabled; }

protected:
	void fillObjectsBuffer(const ObjectBaseList & ol, RenderPass pass);
	void renderObjects(Scene & scene, RenderPass pass);
	void renderLight(int first_wr_buff, bool clear_only);
	void renderConeShadows();
	void renderOmniShadows();
	void renderConeShadow(int index, Light * light);
	void renderOmniShadow(int index, Light * light, QOpenGLShaderProgram * prog, QMatrix4x4 om);

	bool bindShader(ShaderRole role, QOpenGLShaderProgram ** ret = 0);
	bool bindShader(QOpenGLShaderProgram * sp);
	void initShaders();
	void releaseShader();

	QVector<int> getFreePlanes(int count);

private:
	float gamma_ = 1.f;
	int camera_light_mode, cur_write_plane = 0, prev_write_plane = 0;
	bool edit_mode, need_init_shaders, need_render_sum, __reinit_debug;
	Framebuffer fbo_ds, fbo_out;
	QMap<ShaderRole, QString> shader_files;
	QMap<ShaderRole, QStringList> shader_defines;
	QMap<ShaderRole, QOpenGLShaderProgram *> shaders;
	QOpenGLShaderProgram * shader_fxaa = nullptr;

	RendererMaterial rend_mat;
	RendererService rend_service;
	RendererSelection rend_selection;
	TonemappingProc tone_proc;

	Mesh * quad;
	Light * cam_light;
	CubeTexture tex_env;

	QPoint mouse_pos;
	QRect mouse_rect;
	QMatrix4x4 prev_view, prev_proj;
	QMatrix4x4 mat_norm_to_tex_coord, mat_camera_fullview_inv, mat_proj_90;
	QMatrix4x4 mat_faces[6];
	QMatrix3x3 nm;
	QVector4D corner_dirs[4];
	QVector<QVector3D> hcontent;
	QMap<int, QList<Light *>> cur_lights;
	QVector<FramebufferEffectBase *> fb_effects;
	QImage last_img;
	QString obr_defines, timings;
	bool shadows_enabled = true;
	bool is_grabbing     = false;
};

#endif // RENDERER_H
