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

#include "glparticles_system.h"


GLParticlesSystem::GLParticlesSystem(const QVector3D & pos): GLObjectBase() {
	pass_           = GLObjectBase::Transparent;
	freq            = 40.f;
	birthRate_      = 10.f;
	lifeDuration_   = 2.f;
	fade_time       = 0.5f;
	size_           = 1.f;
	additionalSpeed = 0.f;
	initialSpeed_   = 1.f;
	need_birth      = -1.f;
	tex_rect.setRect(0., 0., 1., 1.);
	tex_scale        = QSizeF();
	emitterPosition_ = pos;
	emitterDirection_.setZ(1.);
	speedDirection_.setZ(1.);
	speedDecay_ = initialAngle_ = enlargeSpeed_ = enlargeSpeedJitter_ = baseAngle_ = 0.f;
	lifeDurationJitter_ = speedJitter_ = speedDirectionJitter_ = sizeJitter_ = angleJitter_ = 0.f;
	active_ = birthEnabled_ = true;
	is_diffuse_anim = add_vert_face = false;
	emitterType_                    = Cone;
	tick_life                       = 1.f / freq;
	tick_birth                      = birthRate_ / freq;
}


void GLParticlesSystem::update() {
	// qDebug() << "update" << need_birth << tick_birth;
	if (!active_) return;
	// QMutexLocker locker(&mutex);
	Particle cp(lifeDuration_);
	if (birthEnabled_) need_birth += tick_birth;
	qDebug() << "update" << particles.size();
	if (need_birth >= 1.f) {
		cp.pos        = emitterPosition_;
		// qDebug() << "speed" << cp.speed;
		cp.speedDecay = 1.f + speedDecay_;
		for (int i = 0; i < floor(need_birth); ++i) {
			cp.lifeDuration = lifeDuration_ + urand(lifeDurationJitter_);
			switch (emitterType_) {
			case Omni: cp.speed = QVector3D(urand(), urand(), urand()).normalized() * initialSpeed_ * (1.f + urand(speedJitter_)); break;
			case Cone:
			case Box:
				cp.speed = emitterDirection_ * initialSpeed_ * (1.f + urand(speedJitter_));
				cp.speed += orthToVector(cp.speed, speedDirectionJitter_);
				break;
			}
			if (emitterType_ == Box) cp.pos = emitterRect_.randomPoint();
			// qDebug() << "before" << cp.speed.length();
			lengthenVector(cp.speed, additionalSpeed);
			// qDebug() << "after" << cp.speed.length();
			cp.size         = size_ + urand(sizeJitter_);
			cp.angle        = initialAngle_ + urand(angleJitter_);
			cp.enlargeSpeed = (enlargeSpeed_ + urand(enlargeSpeedJitter_)) * tick_life;
			/*if (is_diffuse_anim) {
			    if (material_.diffuse.animation_frame_rate < 0 && animation->bitmaps.size() > 0)
			        cp.animationFrameRate = animation->bitmaps.size() / cp.lifeDuration;
			    else
			        cp.animationFrameRate = material_.diffuse.animation_frame_rate;
			}*/
			if (tex_scale.isEmpty())
				cp.tex_rect.setSize(tex_rect.size());
			else
				cp.tex_rect.setSize(tex_rect.size() * tex_scale);
			cp.tex_rect.moveTopLeft(tex_rect.topLeft() + QPointF(uprand(tex_rect.width() - cp.tex_rect.width()),
			                                                     uprand(tex_rect.height() - cp.tex_rect.height())));
			// cp.tex_rect = tex_rect;
			particles.push_back(cp);
		}
		need_birth -= floor(need_birth);
	}
	for (int i = 0; i < particles.size(); ++i) {
		Particle & c(particles[i]);
		foreach(const QVector3D & f, forces)
			c.speed += f;
		c.lifeCurrent += tick_life;
		// qDebug() << "life" << c.lifeCurrent << c.lifeDuration;
		if (c.lifeCurrent > c.lifeDuration) {
			// qDebug() << "remove" << i;
			particles.remove(i);
			i--;
			continue;
		}
		c.pos += c.speed * tick_life;
		c.speed /= c.speedDecay;
		c.size += c.enlargeSpeed;
		// if (c.lifeCurrent > 1.) c.angle += urand(5.);
	}
}


void GLParticlesSystem::draw(QOpenGLShaderProgram * prog, bool) {
	if (particles.isEmpty()) return;
	if (view_ == nullptr) return;
	pass_ = GLObjectBase::Transparent;
	Camera * camera(view_->camera());
	QVector3D apos = camera->pos(), dir = camera->direction();
	// qDebug() << dir;
	// qDebug() << camera.angles();
	// qDebug() << camera.angle_xy;
	GLfloat cxyc, czs, czc;
	GLfloat dx, dy, cdx, cdy, cdz, a, tr_r = material_.color_diffuse.redF(), tr_g = material_.color_diffuse.greenF(),
									  tr_b = material_.color_diffuse.blueF(),
									  tr_a = material_.color_diffuse.alphaF() * (1.f - material_.transparency);
	// cxys = sin(camera.angle_xy * deg2rad);
	cxyc                                   = cosf(camera->angles_.y() * deg2rad);
	czs                                    = sinf(camera->angles_.z() * deg2rad);
	czc                                    = cosf(camera->angles_.z() * deg2rad);

	dx                                     = -czc;
	dy                                     = czs;
	vertices.clear();
	texcoords.clear();
	colors.clear();
	for (int i = 0; i < particles.size(); ++i)
		// particles[i].pos_h.setZ((particles[i].pos - apos).lengthSquared());
		particles[i].pos_h.setZ(particles[i].pos.distanceToPlane(apos, dir));
	std::sort(particles.begin(), particles.end());
	glBegin(GL_POINTS);
	foreach(const Particle & i, particles) {
		// glVertex3f(i.pos.x(), i.pos.y(), i.pos.z());
		a = (i.lifeDuration - i.lifeCurrent) / fade_time;
		if (a > 1.f) a = 1.f;
		a *= tr_a;
		cdx = dx * i.size;
		cdy = dy * i.size;
		cdz = i.size;
		vertices << i.pos.x() - cdx << i.pos.y() - cdy << i.pos.z() - cdz;
		vertices << i.pos.x() - cdx << i.pos.y() - cdy << i.pos.z() + cdz;
		vertices << i.pos.x() + cdx << i.pos.y() + cdy << i.pos.z() + cdz;
		vertices << i.pos.x() + cdx << i.pos.y() + cdy << i.pos.z() - cdz;
		cdx = i.size;
		cdy = i.size;
		texcoords << i.tex_rect.right() << i.tex_rect.top();
		texcoords << i.tex_rect.right() << i.tex_rect.bottom();
		texcoords << i.tex_rect.left() << i.tex_rect.bottom();
		texcoords << i.tex_rect.left() << i.tex_rect.top();
		colors << tr_r << tr_g << tr_b << a;
		colors << tr_r << tr_g << tr_b << a;
		colors << tr_r << tr_g << tr_b << a;
		colors << tr_r << tr_g << tr_b << a;
		if (add_vert_face) {
			if (cxyc > 0.f) {
				vertices << i.pos.x() - cdx << i.pos.y() - cdy << i.pos.z();
				vertices << i.pos.x() + cdx << i.pos.y() - cdy << i.pos.z();
				vertices << i.pos.x() + cdx << i.pos.y() + cdy << i.pos.z();
				vertices << i.pos.x() - cdx << i.pos.y() + cdy << i.pos.z();
			} else {
				vertices << i.pos.x() - cdx << i.pos.y() - cdy << i.pos.z();
				vertices << i.pos.x() - cdx << i.pos.y() + cdy << i.pos.z();
				vertices << i.pos.x() + cdx << i.pos.y() + cdy << i.pos.z();
				vertices << i.pos.x() + cdx << i.pos.y() - cdy << i.pos.z();
			}
			texcoords << i.tex_rect.right() << i.tex_rect.top();
			texcoords << i.tex_rect.right() << i.tex_rect.bottom();
			texcoords << i.tex_rect.left() << i.tex_rect.bottom();
			texcoords << i.tex_rect.left() << i.tex_rect.top();
			colors << tr_r << tr_g << tr_b << a;
			colors << tr_r << tr_g << tr_b << a;
			colors << tr_r << tr_g << tr_b << a;
			colors << tr_r << tr_g << tr_b << a;
		}
	}
	glEnd();
	// bool cae = glIsEnabled(GL_COLOR_ARRAY), nae = glIsEnabled(GL_NORMAL_ARRAY);
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glEnableClientState(GL_COLOR_ARRAY);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	// glNormal3f(vn.x(), vn.y(), vn.z());
	glNormal3f(0., 0., 1.);
	glDepthMask(false);
	glEnable(GL_COLOR_ARRAY);
	glDisable(GL_NORMAL_ARRAY);
	glVertexPointer(3, GL_FLOAT, 0, vertices.constData());
	glTexCoordPointer(2, GL_FLOAT, 0, texcoords.constData());
	glColorPointer(4, GL_FLOAT, 0, colors.constData());
	// glEnable(GL_ALPHA_TEST);
	// glAlphaFunc();
	glDrawArrays(GL_QUADS, 0, vertices.size() / 3);
	glDepthMask(true);
	// glDisable(GL_ALPHA_TEST);
	// if (!cae) glDisable(GL_COLOR_ARRAY);
	// if (nae) glEnable(GL_NORMAL_ARRAY);
}


GLParticlesSystem::Particle::Particle(float life_dur) {
	size  = 1.;
	angle = lifeCurrent = 0.;
	speedDecay          = 0.;
	lifeDuration        = life_dur;
	tex_rect            = QRectF(0, 0, 1, 1);
}
