/*
    QGL Scene
    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 GLSCENE_H
#define GLSCENE_H

#include "gltypes.h"


class QGLENGINE_CORE_EXPORT Scene: public QObject {
	Q_OBJECT
	friend class RendererBase;
	friend class Renderer;
	friend class RendererMaterial;
	friend class RendererService;
	friend class RendererSelection;
	friend class ObjectBase;
	friend class Light;
	friend QGLENGINE_CORE_EXPORT QDataStream & operator<<(QDataStream & s, const Scene * p);
	friend QGLENGINE_CORE_EXPORT QDataStream & operator>>(QDataStream & s, Scene *& p);

public:
	explicit Scene();
	virtual ~Scene();

	enum SelectionMode {
		smNoSelection,
		smSingleSelection,
		smMultiSelection,
	};

	Q_ENUMS(SelectionMode)

	QString name() const { return name_; }
	void setName(const QString & name) { name_ = name; }

	bool prepare();

	Scene * clone();

	/// Add object \"o\" to scene and take its ownership
	/// All materials and geometries used by \"o\" tree
	/// copied into this scene
	void addObject(ObjectBase * o, ObjectBase * parent = nullptr);

	void addScene(const Scene * s);
	void assignFrom(const Scene * s);
	void clear();
	void reinitAll();

	int objectsCount(bool all = false);
	ObjectBaseList objects(bool all = false);
	ObjectBase * rootObject() { return root_; }
	void removeObject(ObjectBase * o, bool inChildren = true);
	void removeObject(ObjectBase & o, bool inChildren = true);
	void clearObjects(bool deleteAll = false);

	SelectionMode selectionMode() const { return sel_mode_; }
	void setSelectionMode(SelectionMode mode) { sel_mode_ = mode; }
	void selectObject(ObjectBase * o, bool add_to_selection = false);
	void selectObjects(ObjectBaseList ol, bool add_to_selection = false);
	void selectObjectsByMesh();
	void selectObjectsByMaterial();
	void clearSelection();
	ObjectBaseList selectedObjects(bool top_only = false) const;
	ObjectBase * selectedObject() const;
	void cleanUnused();

	const Box3D & boundingBox() const;

	QVector<Material *> getMaterials() const { return materials; }
	Material * newMaterial(const QString & name = QString());
	void removeMaterial(Material * m);
	void makeMaterialsUniqueNames();

	void removeLight(Light * l);

	void dump();
	void destroy(QOpenGLExtraFunctions * f);
	void destroyUnused(QOpenGLExtraFunctions * f);

	void setLightsChanged() { lights_changed = tree_changed = true; }

protected:
	void prepareTree(ObjectBase * o);
	void gatherSelection();
	void objectsCountInternal(int * cnt, ObjectBase * where);
	void removeObjectInternal(ObjectBase * o, ObjectBase * where);
	void emitSelectionChanged();
	QString uniqueName(QString n, const QSet<QString> & names);

	void attachObject(ObjectBase * o);
	Mesh * attachMesh(Mesh * mesh);
	void setTreeChanged();
	void setTreeStructChanged();
	void setMaterialsChanged() { mat_changed = true; }
	void setObjectMeshChanged(ObjectBase * o);


	QString name_;
	ObjectBase * root_;
	bool tree_changed, mat_changed, lights_changed, destroying;
	bool need_reload_materials, tree_struct_changed;
	QVector<bool> mat_map_changed;

	QVector<Mesh *> geometries, td_geometries;
	QVector<Material *> materials;

	QMap<int, QMap<Mesh *, ObjectBaseList>> geometries_used;
	QMap<int, QList<Light *>> lights_used;
	QList<Camera *> cameras_used;
	QVector<Material *> changed_materials;

	SelectionMode sel_mode_;
	ObjectBaseList selected_, selected_top;

protected slots:

signals:
	void __objectDeleted(ObjectBase * o);
	void __destroyed();
	void treeChanged();
	void selectionChanged();
};


QGLENGINE_CORE_EXPORT QDataStream & operator<<(QDataStream & s, const Scene * p);
QGLENGINE_CORE_EXPORT QDataStream & operator>>(QDataStream & s, Scene *& p);

#endif // GLSCENE_H
