/*
    QGL Mesh
    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 GLMESH_H
#define GLMESH_H

#include "glvertexobject.h"

#include <chunkstream.h>


class QGLENGINE_CORE_EXPORT Mesh {
	friend QDataStream & operator<<(QDataStream & s, const Mesh * m);
	friend QDataStream & operator>>(QDataStream & s, Mesh *& m);

public:
	Mesh(GLenum geom_type_ = GL_TRIANGLES);
	~Mesh();

	Mesh * clone();

	void init(QOpenGLExtraFunctions * f);
	void destroy(QOpenGLExtraFunctions * f);
	bool rebuffer(QOpenGLExtraFunctions * f);
	void draw(QOpenGLExtraFunctions * f, int count, int type = 0);
	void reinit();
	void clear();
	void loadObject(QOpenGLExtraFunctions * f, const QGLEngineShaders::Object & object, int type = 0);
	void loadObjects(QOpenGLExtraFunctions * f, const QVector<QGLEngineShaders::Object> & objects, int type = 0);
	void loadSelections(QOpenGLExtraFunctions * f, const QVector<uchar> & sels, int type = 0);

	int verticesCount() const { return vertices_.size(); }
	int trianglesCount() const { return triangles_.size(); }
	int linesCount() const { return lines_.size(); }
	bool isInit() const { return buffer_geom.isInit(); }
	bool isEmpty() const { return vertices_.isEmpty(); }
	uint hash() const;

	bool isObjectsChanged(int type = 0) const;
	bool isSelectionChanged(int type = 0) const;
	void setObjectsChanged(int type = 0, bool yes = true);
	void setSelectionChanged(int type = 0, bool yes = true);
	void setAllObjectsChanged(bool yes = true);
	void setAllSelectionChanged(bool yes = true);

	QVector<QVector3D> & vertices() {
		changed = hash_changed = true;
		return vertices_;
	}
	QVector<QVector3D> & normals() {
		changed = hash_changed = true;
		return normals_;
	}
	QVector<QVector2D> & texcoords() {
		changed = hash_changed = true;
		return texcoords_;
	}
	QVector<Vector3i> & indicesTriangles() {
		changed = hash_changed = true;
		return triangles_;
	}
	QVector<Vector2i> & indicesLines() {
		changed = hash_changed = true;
		return lines_;
	}

	void translatePoints(const QVector3D & dp);
	void translatePoints(const double & x, const double & y, const double & z) { translatePoints(QVector3D(x, y, z)); }
	void scalePoints(const QVector3D & dp);
	void scalePoints(const double & s) { scalePoints(QVector3D(s, s, s)); }
	void rotatePoints(const double & angle, const QVector3D & a);
	void rotatePoints(const double & angle, const double & x, const double & y, const double & z) {
		rotatePoints(angle, QVector3D(x, y, z));
	}
	void transformPoints(const QMatrix4x4 & mat);
	void flipNormals();
	void append(const Mesh * m);

	bool saveToFile(const QString & filename);
	bool loadFromFile(const QString & filename);

	Box3D boundingBox() const;

private:
	void calculateNormals();
	void calculateTangents();
	VertexObject * vaoByType(int type);

	QVector<QVector3D> vertices_, normals_, tangents_, bitangents_;
	QVector<QVector2D> texcoords_;
	QVector<Vector3i> triangles_;
	QVector<Vector2i> lines_;

	QVector<QGLEngineShaders::Vertex> data_;
	GLenum geom_type;
	Buffer buffer_geom, buffer_ind;
	QMap<int, VertexObject *> vao_map;
	mutable uint hash_;
	mutable bool hash_changed;
	int vert_count;
	bool changed;
};


QGLENGINE_CORE_EXPORT QDataStream & operator<<(QDataStream & s, const Mesh * m);
QGLENGINE_CORE_EXPORT QDataStream & operator>>(QDataStream & s, Mesh *& m);

#endif // GLMESH_H
