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

#ifndef WATER_SYSTEM_H
#define WATER_SYSTEM_H

#include "globject.h"

#define BIG_CHUNK_SIZE  4
#define BIG_CHUNK_SIZEF 4.f
#define CHUNK_SIZE 8*8*8

class WaterSystem: public GLObjectBase
{
public:
	WaterSystem();
	~WaterSystem();

	virtual void draw(QGLShaderProgram * prog, bool simplest = false);
	virtual void update();
	virtual void init() {createGLTexture(texture, QImage("./media-record.png")); material_.map_diffuse.bitmap_id = texture; is_init = true;}

protected:
	class Chunk;
	class BigChunk;
	struct Particle {
		Particle() {mass = 1.; radius = 0.2;}
		//Particle(const Particle & other) {pos = other.pos; speed = other.speed; accel = other.accel; mass = other.mass; radius = other.radius; processed = other.processed;}

		Vector3d pos;
		Vector3d speed;
		Vector3d accel;
		float mass;
		float radius;
		bool processed;

		Vector3i chunkIndex() {return Vector3i(floor(pos.x / 8.), floor(pos.y / 8.), floor(pos.z / 8.));}
		Vector3i bigChunkIndex() {Vector3i i(floor(pos.x / 8.), floor(pos.y / 8.), floor(pos.z / 8.)); return Vector3i(floor(i.p0 / BIG_CHUNK_SIZEF), floor(i.p1 / BIG_CHUNK_SIZEF), floor(i.p2 / BIG_CHUNK_SIZEF));}
		void process(const QList<QVector<Particle> * > & near_parts);
	};
	class Chunk {
	public:
		Chunk() {particles.reserve(CHUNK_SIZE); prev_particles.reserve(CHUNK_SIZE);}
		Vector3i bigIndex() {return Vector3i(floor(index.p0 / BIG_CHUNK_SIZEF), floor(index.p1 / BIG_CHUNK_SIZEF), floor(index.p2 / BIG_CHUNK_SIZEF));}
		void saveState() {prev_particles = particles; for (int i = 0; i < particles.size(); ++i) particles[i].processed = false;}
		Vector3i index;
		QVector<Particle> particles, prev_particles;
	};
	class BigChunk {
	public:
		BigChunk() {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) chunks[i][j][k] = 0;}
		~BigChunk() {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) if (chunks[i][j][k] != 0) delete chunks[i][j][k];}
		void saveState() {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) if (chunks[i][j][k] != 0) chunks[i][j][k]->saveState();}
		Chunk * newChunk(const Vector3i & i) {
			int _i = i.p0 % BIG_CHUNK_SIZE, _j = i.p1 % BIG_CHUNK_SIZE, _k = i.p2 % BIG_CHUNK_SIZE;
			if (_i < 0) _i += BIG_CHUNK_SIZE; if (_j < 0) _j += BIG_CHUNK_SIZE; if (_k < 0) _k += BIG_CHUNK_SIZE;
			if (chunks[_i][_j][_k] == 0) {qDebug() << "insert" << _i << _j << _k; chunks[_i][_j][_k] = new Chunk();}
			return chunks[_i][_j][_k];}
		Vector3i index;
		Chunk * chunks[BIG_CHUNK_SIZE][BIG_CHUNK_SIZE][BIG_CHUNK_SIZE];
	};

	BigChunk * newBigChunk(const Vector3i & i) {BigChunk * tbc = big_chunks.value(i); if (tbc == 0) {qDebug() << "insert big" << i; tbc = new BigChunk(); big_chunks.insert(i, tbc);} return tbc;}

	QHash<Vector3i, BigChunk * > big_chunks;
	QList<Vector3i> near_offsets;
	GLuint texture;
	ullong part_count;

};

#endif // WATER_SYSTEM_H
