/*
	QGLView
	Copyright (C) 2020  Ivan Pelipenko peri4ko@yandex.ru

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU 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 General Public License for more details.

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

#ifndef QGLVIEW_H
#define QGLVIEW_H

#include "openglwindow.h"
#include "glframebuffer.h"
#include "glprimitives.h"
#include "glcamera.h"
#include "glrendererbase.h"
#include "glscene.h"
#include "renderer.h"
#include "mouse_controller.h"
#include <QTime>
#include <QMenu>


class QGLView: public OpenGLWindow
{
	friend class MouseController;
	friend class GLRendererBase;
	friend class TextureManager;
	friend class ObjectBase;
	friend class Scene;
	friend class RendererBase;
	friend class Renderer;
	friend class RendererMaterial;
	friend class RendererService;
	friend class RendererSelection;
	friend class TonemappingProc;
	Q_OBJECT
	Q_PROPERTY (QColor backColor READ backColor WRITE setBackColor)
	Q_PROPERTY (float lineWidth READ lineWidth WRITE setLineWidth)
	Q_PROPERTY (float FOV READ FOV WRITE setFOV)
	Q_PROPERTY (float depthStart READ depthStart WRITE setDepthStart)
	Q_PROPERTY (float gamma READ gamma WRITE setGamma)
	Q_PROPERTY (bool autoExposure READ autoExposure WRITE setAutoExposure)
	Q_PROPERTY (QColor ambientColor READ ambientColor WRITE setAmbientColor)
	Q_PROPERTY (QColor fogColor READ fogColor WRITE setFogColor)
	Q_PROPERTY (bool fogEnabled READ isFogEnabled WRITE setFogEnabled)
	Q_PROPERTY (float fogDensity READ fogDensity WRITE setFogDensity)
	Q_PROPERTY (float fogDecay READ fogDecay WRITE setFogDecay)
	Q_PROPERTY (int renderMode READ renderMode WRITE setRenderMode)
	Q_PROPERTY (bool grabMouse READ isGrabMouseEnabled WRITE setGrabMouseEnabled)
	Q_PROPERTY (bool mouseRotate READ isMouseRotateEnabled WRITE setMouseRotateEnabled)
	Q_PROPERTY (bool mouseSelection READ isMouseSelectionEnabled WRITE setMouseSelectionEnabled)
	Q_PROPERTY (bool cameraOrbit READ isCameraOrbit WRITE setCameraOrbit)
	Q_PROPERTY (bool hoverHalo READ isHoverHaloEnabled WRITE setHoverHaloEnabled)
	Q_PROPERTY (QColor hoverHaloColor READ hoverHaloColor WRITE setHoverHaloColor)
	Q_PROPERTY (float hoverHaloFillAlpha READ hoverHaloFillAlpha WRITE setHoverHaloFillAlpha)
	Q_PROPERTY (bool selectionHalo READ isSelectionHaloEnabled WRITE setSelectionHaloEnabled)
	Q_PROPERTY (QColor selectionHaloColor READ selectionHaloColor WRITE setSelectionHaloColor)
	Q_PROPERTY (float selectionHaloFillAlpha READ selectionHaloFillAlpha WRITE setSelectionHaloFillAlpha)
	Q_PROPERTY (Qt::MouseButton selectionButton READ selectionButton WRITE setSelectionButton)
	Q_PROPERTY (Qt::KeyboardModifier selectionModifier READ selectionModifier WRITE setSelectionModifier)
	Q_PROPERTY (Scene::SelectionMode selectionMode READ selectionMode WRITE setSelectionMode)

public:
	QGLView();
	virtual ~QGLView();

	enum CameraLightMode {
		clmOff,
		clmAuto,
		clmOn,
	};

	enum Feature {
		qglFXAA,
		qglAnisotropicLevel,
		qglEyeAccomodationEnabled,
		qglEyeAccomodationTime,
		qglEyeAccomodationMaxSpeed,
		qglBloomEnabled,
		qglBloomThreshold,
		qglBloomFactor,
		qglBloomRadius,
		qglMotionBlurEnabled,
		qglMotionBlurFactor,
		qglMotionBlurSteps,
		qglShadowsEnabled,
		qglShadowsMapSize,
		qglShadowsSoftEnabled,
		qglReflectionsEnabled,
		qglReflectionsBlur,
		qglSSAOEnabled,
		qglSSAORadius,
		qglDepthOfFieldEnabled,
		qglDepthOfFieldAutoFocusEnabled,
		qglDepthOfFieldAutoFocusSpeed,
		qglDepthOfFieldFocus,
		qglDepthOfFieldDiaphragm
	};

	Q_ENUM(CameraLightMode)

	void stop();
	void start(float freq = 60.);

	QColor backColor() const {return backColor_;}
	float lineWidth() const {return lineWidth_;}
	float FOV() const {return camera()->fov_;}
	float depthStart() const {return camera()->depth_start;}
	float currentFPS() const {return fps_;}
	float gamma() const {return renderer_.gamma_;}
	bool autoExposure() const {return renderer_.tone_proc.enabled;}
	int maxAnisotropicLevel() const {return max_anisotropic;}
	QString environmentMapFile() const {return renderer_.tex_env.fileHDR();}

	QColor ambientColor() const {return ambientColor_;}
	QColor fogColor() const {return fogColor_;}
	float  fogDensity() const {return fogDensity_;}
	float  fogDecay() const {return fogDecay_;}
	bool isFogEnabled() const {return fogEnabled_;}
	bool isLightEnabled() const {return lightEnabled_;}
	bool isGrabMouseEnabled() const {return mouse.isGrabMouseEnabled();}
	bool isMouseRotateEnabled() const {return mouse.isMouseRotateEnabled();}
	bool isMouseSelectionEnabled() const {return mouse.isMouseSelectionEnabled();}
	bool isCameraOrbit() const {return mouse.isCameraOrbit();}
	bool isHoverHaloEnabled() const {return hoverHalo_;}
	QColor hoverHaloColor() const {return hoverHaloColor_;}
	float hoverHaloFillAlpha() const {return hoverHaloFill_;}
	bool isSelectionHaloEnabled() const {return selectionHalo_;}
	QColor selectionHaloColor() const {return selectionHaloColor_;}
	float selectionHaloFillAlpha() const {return selectionHaloFill_;}

	QVariant feature(Feature f) const {return features_.value(int(f));}
	QVariant setFeature(Feature f, const QVariant & value) {QVariant ret = features_.value(int(f)); features_[int(f)] = value; return ret;}
	bool isFeatureEnabled(Feature f) const {return features_[int(f)].toBool();}

	int renderMode() const {return (int)rmode;}
	void setRenderMode(int mode) {rmode = (ObjectBase::RenderMode)mode;}

	bool isServiceMode() const {return renderer_.edit_mode;}
	void setServiceMode(bool yes) {renderer_.edit_mode = yes;}

//	void addObject(GLObjectBase & o) {addObject(&o);}

	Scene::SelectionMode selectionMode() const {return scene_->selectionMode();}
	Qt::MouseButton selectionButton() const {return mouse.selectionButton();}
	Qt::KeyboardModifier selectionModifier() const {return mouse.selectionModifier();}

	void setSelectionMode(Scene::SelectionMode m) {scene_->setSelectionMode(m);}
	void setSelectionButton(Qt::MouseButton v) {mouse.setSelectionButton(v);}
	void setSelectionModifier(Qt::KeyboardModifier v) {mouse.setSelectionModifier(v);}

	void selectObject(ObjectBase * o, bool add_to_selection = false) {scene_->selectObject(o, add_to_selection);}
	void clearSelection() {scene_->clearSelection();}
	ObjectBaseList selectedObjects(bool top_only = false) const {return scene_->selectedObjects(top_only);}
	QList<Light  * > selectedLights() const;
	QList<Camera * > selectedCameras() const;
	ObjectBase * selectedObject() const {return scene_->selectedObject();}

	TextureManager * textureManager() {return renderer_.textures_manager;}
	void reloadTextures() {renderer_.markReloadTextures();}

	Scene * scene() {return scene_;}
	void focusOn(const Box3D & bb);
	void setCameraLightMode(CameraLightMode m) {renderer_.setCameraLightMode(m);}
	CameraLightMode cameraLightMode() const {return (CameraLightMode)renderer_.cameraLightMode();}

	Camera * camera() {return camera_;}
	const Camera * camera() const {return camera_;}
	void setCamera(Camera * camera) {camera_ = camera;}
	void setDefaultCamera() {camera_ = default_camera;}
	QByteArray saveCamera();
	void restoreCamera(const QByteArray & ba);
	QByteArray saveFeatures();
	void restoreFeatures(const QByteArray & ba);

	QImage materialThumbnail(Material * m) {return renderer_.materialThumbnail(m);}
	void setCurrentAction(RendererService::HandleAction ha) {renderer_.rend_service.setCurrentAction(ha);}
	void setContextActions(QList<QAction*> al) {context_menu.clear(); context_menu.addActions(al);}

	GLfloat aspect, iaspect;
	Renderer renderer_;

protected:
	void render();
	void resizeEvent(QResizeEvent * e);

	void timerEvent(QTimerEvent * );
	void initialize();
	void resizeGL(int width, int height);
	void mousePressEvent(QMouseEvent * e) {mouse.mousePressEvent(e);}
	void mouseMoveEvent(QMouseEvent * e) {mouse.mouseMoveEvent(e);}
	void mouseReleaseEvent(QMouseEvent * e) {mouse.mouseReleaseEvent(e);}
	void wheelEvent(QWheelEvent * e) {mouse.wheelEvent(e);}
	void mouseDoubleClickEvent(QMouseEvent * e) {mouse.mouseDoubleClickEvent(e);}
	void leaveEvent(QEvent * );

	void keyPressEvent(QKeyEvent * e);
	void keyReleaseEvent(QKeyEvent * e);
	void focusOutEvent(QFocusEvent *);

	void checkCaps();

private:
	void processKeys();
	bool setupViewport();

	Scene * scene_;
	Camera * camera_, * default_camera;
	MouseController mouse;
	QMenu context_menu;
//	uint cid;
	QSet<int> keys_;
	QColor backColor_, fogColor_, ambientColor_, hoverHaloColor_, selectionHaloColor_;
	QTime time, ktm_;
	GLint max_anisotropic, max_texture_chanels;
	ObjectBase::RenderMode rmode;
	GLRendererBase::RenderingParameters start_rp;
	QHash<int, QVariant> features_;
	QSize prev_size;
	float lineWidth_;
	float fps_, fps_tm, fogDensity_, fogDecay_;
	float hoverHaloFill_, selectionHaloFill_, m_motionBlurFactor;
	int timer, fps_cnt, sh_id_loc, deleting_;
	bool is_first_draw, is_init, fogEnabled_, lightEnabled_;
	bool shaders_supported, changed_, need_init_;
	bool hoverHalo_, selectionHalo_, shaders_bind;

private slots:
	void __destroyed();
	void __objectDeleted(ObjectBase * o);

public slots:
	void setBackColor(const QColor & arg) {backColor_ = arg;}
	void setLineWidth(const float & arg) {lineWidth_ = arg;}
	void setFOV(const float & arg) {camera()->fov_ = arg;}
	void setDepthStart(const float & arg) {camera()->depth_start = arg;}
	void setGamma(const float & arg) {renderer_.gamma_ = arg;}
	void setAutoExposure(bool arg) {renderer_.tone_proc.enabled = arg;}
	void setAmbientColor(const QColor & arg) {ambientColor_ = arg;}
	void setEnvironmentMapFile(QString file) {renderer_.tex_env.setFileHDR(file); renderer_.recreateMaterialThumbnails(true);}
	void setFogColor(const QColor & arg) {fogColor_ = arg;}
	void setFogDensity(const float & arg) {fogDensity_ = arg;}
	void setFogDecay(const float & arg) {fogDecay_ = arg;}
	void setFogEnabled(const bool & arg) {fogEnabled_ = arg;}
	void setLightEnabled(const bool & arg) {lightEnabled_ = arg;}
	void setGrabMouseEnabled(const bool & arg) {mouse.setGrabMouseEnabled(arg);}
	void setMouseRotateEnabled(const bool & arg) {mouse.setMouseRotateEnabled(arg);}
	void setMouseSelectionEnabled(const bool & arg) {mouse.setMouseSelectionEnabled(arg);}
	void setCustomMouseMove(const bool & arg) {mouse.setCustomMouseMove(arg);}
	void setCameraOrbit(const bool & arg) {mouse.setCameraOrbit(arg);}
	void setHoverHaloEnabled(const bool & arg) {hoverHalo_ = arg;}
	void setHoverHaloColor(const QColor & arg) {hoverHaloColor_ = arg;}
	void setHoverHaloFillAlpha(const float & arg) {hoverHaloFill_ = arg;}
	void setSelectionHaloEnabled(const bool & arg) {selectionHalo_ = arg;}
	void setSelectionHaloColor(const QColor & arg) {selectionHaloColor_ = arg;}
	void setSelectionHaloFillAlpha(const float & arg) {selectionHaloFill_ = arg;}

	void reloadShaders() {renderer_.reloadShaders();}

signals:
	void glBeginPaint();
	void glEndPaint();
	void glKeyPressEvent(QKeyEvent * e);
	void glKeyReleaseEvent(QKeyEvent * e);
	void glMousePressEvent(QMouseEvent * e);
	void glMouseMoveEvent(QMouseEvent * e);
	void glMouseReleaseEvent(QMouseEvent * e);
	void glWheelEvent(QWheelEvent * e);
	void glResize(int, int);
	void glInitializeDone();
	void cameraPosChanged(QVector3D pos);
	void keyEvent(Qt::Key key, Qt::KeyboardModifiers mod);
	void customMouseMoveEvent(QPoint curpos, QPoint lastpos, Qt::MouseButtons buttons);

	void hoverChanged(ObjectBase * cur, ObjectBase * prev);
	void selectionChanged();
	void objectsPositionChanged();
	void materialsChanged();
	void materialThumbnailCreated(Material*);
	void doubleClick();

};

#endif // QGLVIEW_H
