/*
    QGLView
    Copyright (C) 2012  Ivan Pelipenko peri4ko@gmail.com

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

#include "water_system.h"


WaterSystem::WaterSystem(): GLObjectBase() {
	texture = 0;
	near_offsets
	<< Vector3i(-1,  0,  0)
	<< Vector3i( 1,  0,  0)
	<< Vector3i(-1, -1,  0)
	<< Vector3i( 1, -1,  0)
	<< Vector3i( 0, -1,  0)
	<< Vector3i(-1,  1,  0)
	<< Vector3i( 1,  1,  0)
	<< Vector3i( 0,  1,  0)
	<< Vector3i(-1,  0, -1)
	<< Vector3i( 1,  0, -1)
	<< Vector3i( 0,  0, -1)
	<< Vector3i(-1, -1, -1)
	<< Vector3i( 1, -1, -1)
	<< Vector3i( 0, -1, -1)
	<< Vector3i(-1,  1, -1)
	<< Vector3i( 1,  1, -1)
	<< Vector3i( 0,  1, -1)
	<< Vector3i(-1,  0,  1)
	<< Vector3i( 1,  0,  1)
	<< Vector3i( 0,  0,  1)
	<< Vector3i(-1, -1,  1)
	<< Vector3i( 1, -1,  1)
	<< Vector3i( 0, -1,  1)
	<< Vector3i(-1,  1,  1)
	<< Vector3i( 1,  1,  1)
	<< Vector3i( 0,  1,  1);
}


WaterSystem::~WaterSystem() {
	QList<BigChunk * > cv = big_chunks.values();
	foreach (BigChunk * i, cv) {
		qDebug() << "delete" << i;
		delete i;
	}
	cv.clear();
}


void WaterSystem::update() {
	QList<BigChunk * > cv = big_chunks.values();
	//qDebug() << "save states" << cv.size();
	foreach (BigChunk * i, cv)
		i->saveState();
	Particle tp;
	if (part_count < 5000)
	for (int i = 0; i < 10; ++i) {
		tp.pos = QVector3D(urand()*20+30, urand()*20+30, 30);
		tp.speed = QVector3D(urand(1.), urand(1.), 0.);
		newBigChunk(tp.bigChunkIndex())->newChunk(tp.chunkIndex())->particles << tp;
	}
	QList<BigChunk * > near_big;
	QList<Chunk * > near_;
	QList<QVector<Particle> * > near_parts;
	Vector3i cind, nind;
#if QT_VERSION >= 0x040700
	near_big.reserve(27);
	near_.reserve(27);
	near_parts.reserve(27);
#endif
	foreach (BigChunk * bc, cv) {
		Vector3i bci = bc->index;
		near_big.clear();
		near_big << bc;
		foreach (const Vector3i & o, near_offsets)
			near_big << big_chunks.value(bci + o);
		near_big.removeAll(0);
		for (int i = 0; i < BIG_CHUNK_SIZE; ++i)
			for (int j = 0; j < BIG_CHUNK_SIZE; ++j)
				for (int k = 0; k < BIG_CHUNK_SIZE; ++k) {
					Chunk * cc = bc->chunks[i][j][k];
					if (cc == 0) continue;
					near_.clear();
					//foreach (const Vector3i & o, near_offsets)
					//	near_ << big_chunks.value(bci + o);
					near_ << cc;
					near_.removeAll(0);
					near_parts.clear();
					foreach (Chunk * ci, near_)
						if (!ci->prev_particles.isEmpty())
							near_parts << &(ci->prev_particles);
					QVector<Particle> & parts(cc->particles);
					//qDebug() << "chunk" << cc->index.toQVector3D() << "proc" << parts.size() << "particles";
					for (int p = 0; p < parts.size(); ++p) {
						Particle & cp(parts[p]);
						//Vector3i obi = cp.bigChunkIndex(), oi = cp.chunkIndex();
						if (cp.processed) continue;
						cp.processed = true;
						cind = cp.chunkIndex();
						cp.process(near_parts);
						nind = cp.chunkIndex();
						if (nind == cind) continue;
						newBigChunk(cp.bigChunkIndex())->newChunk(nind)->particles << cp;
						parts.remove(p);
						--p;
					}
				}
	}
}


void WaterSystem::Particle::process(const QList<QVector<Particle> * > & near_parts) {
	accel += Vector3d(0., 0., -0.98 / 60.) / mass;
	speed += accel;
	pos += speed;
	speed *= 0.95;
	GLfloat sm = speed.length();//piMax<GLfloat>(fabs(speed.x), fabs(speed.y), fabs(speed.z));
	if (sm > 0.2)
		speed.normalize() *= 0.2;
	if (pos.z - radius < 0.) {
		pos.z = radius;
		speed.z = -speed.z * 0.2;
		speed *= 0.9;
		accel.clear();
	}
	GLfloat sdist, srad, ssrad;
	Vector3d dpos;
	//return;
	/*int cnt = 0;
	foreach (QVector<Particle> * pl, near_parts)
		cnt += pl->size();
	qDebug() << cnt;*/
	foreach (QVector<Particle> * pl, near_parts) {
		QVector<Particle> & rpl(*pl);
		for (int i = 0; i < rpl.size(); ++i) {
			Particle & cp(rpl[i]);
			srad = radius + cp.radius;
			dpos = pos - cp.pos;
			if (fabs(dpos.x) >= srad || fabs(dpos.y) >= srad || fabs(dpos.z) >= srad) continue;
			ssrad = srad * srad;
			sdist = dpos.lengthSquared();
			if (sdist >= ssrad) continue;
			//continue;
			sdist = sqrt(sdist);
			ssrad = (srad - sdist) / 2;
			dpos *= ssrad / sdist;
			//pos += dpos;
			//cp.pos -= dpos;
			//qDebug() << "intersect" << sdist << (pos - cp.pos).length();
			speed -= speed.projectTo(dpos) * 2.;
			cp.speed -= cp.speed.projectTo(dpos) * 2.;
			speed *= 0.8;
			cp.speed *= 0.8;
			accel *= 0.8;
			cp.accel *= 0.8;
		}
	}
}


void WaterSystem::draw(QGLShaderProgram * prog, bool simplest) {
	//return;
	pass_ = GLObjectBase::Transparent;
	//qDebug() << "save states" << cv.size();
	glPointSize(20);
	GLfloat pp[] = {1.f, 1.f, 0.f};
	//glEnable(GL_BLEND);
	glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, pp);
	//glDepthMask(GL_FALSE);
	glNormal3f(0.f, 0.f, 1.f);
	//glEnable(GL_POINT_SPRITE);
	glDisable(GL_TEXTURE_2D);
	glEnable(GL_VERTEX_ARRAY);
	//glDisable(GL_BLEND);
	//glDisable(GL_ALPHA_TEST);
	glDisable(GL_NORMAL_ARRAY);
	glDisable(GL_COLOR_ARRAY);
	glDisable(GL_TEXTURE_COORD_ARRAY);
	double ha = 1. / (big_chunks.size() + 1.), ch = 0.;
	QVector<GLfloat> v;
	QList<BigChunk * > cv = big_chunks.values();
	part_count = 0;
	foreach (BigChunk * bc, cv) {
		for (int i = 0; i < BIG_CHUNK_SIZE; ++i)
			for (int j = 0; j < BIG_CHUNK_SIZE; ++j)
				for (int k = 0; k < BIG_CHUNK_SIZE; ++k) {
					Chunk * cc = bc->chunks[i][j][k];
					if (cc == 0) continue;
					const QVector<Particle> & cp(cc->particles);
					part_count += cp.size();
		v.clear();
					foreach (const Particle & p, cp)
						v << p.pos.x << p.pos.y << p.pos.z;
		qglColor(QColor::fromHsvF(ch, 1., 1.));
		glVertexPointer(3, GL_FLOAT, 0, v.constData());
		glDrawArrays(GL_POINTS, 0, v.size() / 3);
		ch += ha;
		while (ch > 1.) ch -= 1.;
				}
	}
	qDebug() << "draw" << part_count << "water particles";
	//glDepthMask(GL_TRUE);
	/*if (!d_vertices.isEmpty()) {
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		glVertexPointer(3, GL_FLOAT, 0, d_vertices.constData());
		glTexCoordPointer(2, GL_FLOAT, 0, d_uvs.constData());
		//glColorPointer(4, GL_FLOAT, 0, d_colors.constData());
		glNormalPointer(GL_FLOAT, 0, d_normals.constData());
		glDrawArrays(geom_prim, 0, d_vertices.size() / 3);*/
		/*if (pass_ == Reflection) {
			glActiveTexture(GL_TEXTURE1);
			glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
			glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
		}*/
	//}
}
