#pragma once
#include <fstream>
#include <QVector>
#include <QVector2D>
#include <QVector3D>
#include <QMatrix4x4>
#include <QQuaternion>
#include <QOpenGLFunctions>
#include <QOpenGlTexture>
#include <QObject>
#include <QOpenGLTexture>
#include <QRegExp>
#include <..\Header\MainWindow.h>


struct BoundingBox {
	QQuaternion rotation;
	QVector3D center;
	QVector3D extents;
};

struct VertexData
{
	QVector3D position;
	QVector2D texCoord;
	QVector3D normal;
};

struct Segment {
	std::uint32_t textureIndex = 0;
	QVector<VertexData> vertices;
	QVector<GLuint> indices;
};

struct Model {
	std::string name = "";
	std::string parent = "";
	QMatrix4x4 m4x4Translation;
	QQuaternion quadRotation;
	std::vector<Segment*> segmList;
};

struct Material {
	QString name;
	QOpenGLTexture* texture = Q_NULLPTR;
	bool transparent = false;
};

class FileInterface : public QObject
{
	Q_OBJECT

public:
	explicit FileInterface(QString path, QObject *parent)
		: QObject(parent)
		, m_models(new QVector<Model*>)
		, m_materials(new QVector<Material>)
	{
		//open file
		m_file.open(path.toStdString().c_str(), std::ios::in | std::ios::binary);

		if (!m_file.is_open())
			throw std::invalid_argument(std::string("ERROR: file not found: ") += path.toStdString());

		MainWindow* tmp = dynamic_cast<MainWindow*>(parent->parent()->parent());
		if(tmp != NULL)
			connect(this, SIGNAL(sendMessage(QString, int)), tmp, SLOT(printMessage(QString, int)));

		m_filepath = path.left(path.lastIndexOf(QRegExp("/|\\\\")));

	};

	virtual ~FileInterface()
	{
		// close file
		m_file.close();

		//clean up
		for (Model* modelIt : *m_models)
		{
			for (Segment* segIt : modelIt->segmList)
			{
				segIt->indices.clear();
				segIt->vertices.clear();
				delete segIt;
			}
			modelIt->segmList.clear();

			delete modelIt;
		}
		m_models->clear();
		delete m_models;
	};

protected:
	QVector<Model*>* m_models;
	std::fstream m_file;
	QVector<Material>* m_materials;
	BoundingBox m_sceneBbox;
	QString m_filepath;

	virtual void import() = 0;

public:
	virtual QVector<Model*>* getModels() const { return m_models; };
	virtual QVector<Material>* getMaterials() const { return m_materials; };
	virtual BoundingBox getBoundingBox() const { return m_sceneBbox; };

	static Material getDefaultMaterial() {
		Material defMaterial;

		QImage img(1, 1, QImage::Format_RGB32);
		img.fill(Qt::red);

		QOpenGLTexture* new_texture = new QOpenGLTexture(img.mirrored());

		// Set nearest filtering mode for texture minification
		new_texture->setMinificationFilter(QOpenGLTexture::Nearest);

		// Set bilinear filtering mode for texture magnification
		new_texture->setMagnificationFilter(QOpenGLTexture::Linear);

		// Wrap texture coordinates by repeating
		// f.ex. texture coordinate (1.1, 1.2) is same as (0.1, 0.2)
		new_texture->setWrapMode(QOpenGLTexture::Repeat);

		defMaterial.texture = new_texture;

		return defMaterial;
	};

signals:
	void sendMessage(QString msg, int severity);
};