/*
    QGLView
    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 GLPARTICLES_SYSTEM_H
#define GLPARTICLES_SYSTEM_H

#include "glcamera.h"
#include "globject.h"
#include "gltexture_manager.h"

#include <QMutex>

class GLParticlesSystem
	: public QObject
	, public GLObjectBase
	, protected QOpenGLFunctions {
	Q_OBJECT
	Q_PROPERTY(float birthRate READ birthRate WRITE setBirthRate)
	Q_PROPERTY(float lifeDuration READ lifeDuration WRITE setLifeDuration)
	Q_PROPERTY(float size READ size WRITE setSize)
	Q_PROPERTY(float enlargeSpeed READ enlargeSpeed WRITE setEnlargeSpeed)
	Q_PROPERTY(float initialAngle READ initialAngle WRITE setInitialAngle)
	Q_PROPERTY(float initialSpeed READ initialSpeed WRITE setInitialSpeed)
	Q_PROPERTY(float speedDecay READ speedDecay WRITE setSpeedDecay)
	Q_PROPERTY(float baseAngle READ baseAngle WRITE setBaseAngle)
	Q_PROPERTY(QVector3D speedDirection READ speedDirection WRITE setSpeedDirection)
	Q_PROPERTY(QVector3D emitterPosition READ emitterPosition WRITE setEmitterPosition)
	Q_PROPERTY(QVector3D emitterDirection READ emitterDirection WRITE setEmitterDirection)
	Q_PROPERTY(float lifeDurationJitter READ lifeDurationJitter WRITE setLifeDurationJitter)
	Q_PROPERTY(float speedJitter READ speedJitter WRITE setSpeedJitter)
	Q_PROPERTY(float speedDirectionJitter READ speedDirectionJitter WRITE setSpeedDirectionJitter)
	Q_PROPERTY(float sizeJitter READ sizeJitter WRITE setSizeJitter)
	Q_PROPERTY(float enlargeSpeedJitter READ enlargeSpeedJitter WRITE setEnlargeSpeedJitter)
	Q_PROPERTY(float angleJitter READ angleJitter WRITE setAngleJitter)
	Q_PROPERTY(bool active READ isActive WRITE setActive)
	Q_PROPERTY(bool birthEnabled READ isBirthEnabled WRITE setBirthEnabled)
	Q_PROPERTY(float fadeTime READ fadeTime WRITE setFadeTime)

public:
	GLParticlesSystem(const QVector3D & pos = QVector3D());
	~GLParticlesSystem() { ; }

	enum Type {
		Cone,
		Omni,
		Box
	};

	struct Particle {
		Particle(float life_dur = 40.);
		QVector3D pos;
		QVector3D pos_h;
		QVector3D speed;
		QRectF tex_rect;
		float speedDecay;
		float size;
		float angle;
		float enlargeSpeed;
		float lifeDuration;
		float lifeCurrent;
		float animationFrameRate;
	};

	void update();
	void draw(QOpenGLShaderProgram * prog, bool);

	float birthRate() const { return birthRate_; }
	float lifeDuration() const { return lifeDuration_; }
	float size() const { return size_; }
	float enlargeSpeed() const { return enlargeSpeed_; }
	float initialAngle() const { return initialAngle_; }
	float initialSpeed() const { return initialSpeed_; }
	float speedDecay() const { return speedDecay_; }
	float baseAngle() const { return baseAngle_; }
	QVector3D speedDirection() const { return speedDirection_; }
	QVector3D emitterPosition() const { return emitterPosition_; }
	QVector3D emitterDirection() const { return emitterDirection_; }
	Box3D emitterRect() const { return emitterRect_; }
	float lifeDurationJitter() const { return lifeDurationJitter_; }
	float speedJitter() const { return speedJitter_; }
	float speedDirectionJitter() const { return speedDirectionJitter_; }
	float sizeJitter() const { return sizeJitter_; }
	float enlargeSpeedJitter() const { return enlargeSpeedJitter_; }
	float angleJitter() const { return angleJitter_; }
	bool isActive() const { return active_; }
	bool isBirthEnabled() const { return birthEnabled_; }
	GLParticlesSystem::Type emitterType() const { return emitterType_; }
	float fadeTime() const { return fade_time; }
	bool isAddVerticalFaceEnabled() const { return add_vert_face; }

	void setBirthRate(const float & arg) {
		birthRate_ = arg;
		tick_birth = birthRate_ / freq;
	}
	void setLifeDuration(const float & arg) { lifeDuration_ = arg; }
	void setSize(const float & arg) { size_ = arg; }
	void setEnlargeSpeed(const float & arg) { enlargeSpeed_ = arg; }
	void setInitialAngle(const float & arg) { initialAngle_ = arg; }
	void setInitialSpeed(const float & arg) { initialSpeed_ = arg; }
	void setBaseAngle(const float & arg) { baseAngle_ = arg; }
	void setSpeedDecay(const float & arg) { speedDecay_ = arg; }
	void setSpeedDirection(const QVector3D & arg) { speedDirection_ = arg; }
	void setEmitterPosition(const QVector3D & arg) { emitterPosition_ = arg; }
	void setEmitterDirection(const QVector3D & arg) { emitterDirection_ = arg.normalized(); }
	void setEmitterRect(const Box3D & arg) { emitterRect_ = arg; }
	void setLifeDurationJitter(const float & arg) { lifeDurationJitter_ = arg; }
	void setSpeedJitter(const float & arg) { speedJitter_ = arg; }
	void setSpeedDirectionJitter(const float & arg) { speedDirectionJitter_ = arg; }
	void setSizeJitter(const float & arg) { sizeJitter_ = arg; }
	void setEnlargeSpeedJitter(const float & arg) { enlargeSpeedJitter_ = arg; }
	void setActive(const bool & arg) { active_ = arg; }
	void setAngleJitter(const float & arg) { angleJitter_ = arg; }
	void setBirthEnabled(const bool & arg) { birthEnabled_ = arg; }
	void setEmitterType(const GLParticlesSystem::Type & arg) { emitterType_ = arg; }
	void setFadeTime(const float & arg) { fade_time = arg; }
	void setAddVerticalFaceEnabled(const bool & arg) { add_vert_face = arg; }
	void setTextureRect(const QRectF & arg) { tex_rect = arg; }
	void setTextureScale(const float & x, const float & y) { tex_scale = QSizeF(x, y); }
	void setTextureScale(const QSizeF & arg) { tex_scale = arg; }

	void addForce(const QVector3D & f) { forces << f; }
	void birthParticles(int count) { need_birth += count; }

	float frequency() const { return freq; }
	void setFrequency(const float & f) { freq = f; }

	float additionalSpeed;

private:
	QVector3D speedDirection_, emitterPosition_, emitterDirection_;
	QRectF tex_rect;
	QSizeF tex_scale;
	Box3D emitterRect_;
	QMutex mutex;
	GLParticlesSystem::Type emitterType_;
	GLTextureManager::Animation * animation;
	QVector<GLfloat> vertices, texcoords, colors;
	QVector<Particle> particles;
	QVector<QVector3D> forces;
	float birthRate_, initialSpeed_, speedDecay_, lifeDuration_, size_, freq, need_birth, tick_birth, tick_life, fade_time;
	float lifeDurationJitter_, speedJitter_, speedDirectionJitter_, sizeJitter_, angleJitter_, initialAngle_;
	float enlargeSpeed_, enlargeSpeedJitter_, baseAngle_;
	bool active_, birthEnabled_, is_diffuse_anim, add_vert_face;
};

inline bool operator<(const GLParticlesSystem::Particle & f, const GLParticlesSystem::Particle & s) {
	return f.pos_h.z() > s.pos_h.z();
}

#endif // GLPARTICLES_SYSTEM_H
