module gedo.video.staticmesh;



private import std.math;
private import gedo.video.texture;
private import gedo.video.color;
private import gedo.video.ggl;
private import gedo.video.meshfile;
private import gedo.math.vector;
private import gedo.system.cache;



public class StaticMesh
{
public:
	float[2][3] boundingBox;


	static StaticMesh opCall(string path)
	{
		if(!_cache)
		{
			_cache = new typeof(_cache);
		}

		return _cache.get(path,

				(out uint size)
				{
					auto file = new MeshFile(path);

					size = file.meshSize;

					return new StaticMesh(file.vertices, file.faces, file.texture);
				});
	}


	static void clearCache()
	{
		if(_cache)
		{
			_cache.clear();
		}
	}


	static StaticMesh plane()
	{
		static StaticMesh _plane = null;

		if(!_plane)
		{
			GGL_Vertex[4] vertices;
			GGL_Face[2]   faces;

			vertices[0].position[] = cast(float[3])[-0.5, -0.5, 0];
			vertices[1].position[] = cast(float[3])[ 0.5, -0.5, 0];
			vertices[2].position[] = cast(float[3])[ 0.5,  0.5, 0];
			vertices[3].position[] = cast(float[3])[-0.5,  0.5, 0];

			vertices[0].texCoord[] = cast(float[2])[0.0, 0.0];
			vertices[1].texCoord[] = cast(float[2])[1.0, 0.0];
			vertices[2].texCoord[] = cast(float[2])[1.0, 1.0];
			vertices[3].texCoord[] = cast(float[2])[0.0, 1.0];

			faces[0].vertexIndices[] = cast(ushort[3])[0, 1, 2];
			faces[1].vertexIndices[] = cast(ushort[3])[2, 3, 0];

			_plane = new StaticMesh(vertices, faces);
		}

		return _plane;
	}


	this(GGL_Vertex[] vs, GGL_Face[] fs, Texture t = null)
	{
		auto m = GGL_createMesh(vs.ptr, vs.length, fs.ptr, fs.length);

		this(vs, t, m);
	}


	~this()
	{
		GGL_releaseMesh(_mesh);
	}


	void draw(Color c = WHITE)
	{
		draw(_texture, c);
	}


	void draw(Texture t, Color c = WHITE)
	{
		if(t)
		{
			GGL_drawMesh(_mesh, t.gglTexture, &c);
		}
		else
		{
			GGL_drawMesh(_mesh, null, &c);
		}
	}


	Texture texture()
	{
		return _texture;
	}


protected:
	this(GGL_Vertex[] vs, Texture t, GGL_Mesh m)
	{
		_mesh = m;

		_texture = t;

		boundingBox[0][0] = boundingBox[0][1] = vs[0].position[0];
		boundingBox[1][0] = boundingBox[1][1] = vs[0].position[1];
		boundingBox[2][0] = boundingBox[2][1] = vs[0].position[2];

		foreach(v; vs[1 .. length])
		{
			foreach(i, p; v.position)
			{
				if(p < boundingBox[i][0])
				{
					boundingBox[i][0] = p;
				}
				else if(p > boundingBox[i][1])
				{
					boundingBox[i][1] = p;
				}
			}
		}
	}


	static StaticMesh createCircle(float radius, Texture texture)
	{
		GGL_Vertex[17] vertices;
		GGL_Face[16]   faces;

		vertices[0].position[] = 0.0;
		vertices[0].texCoord[] = [0.0f, 0.5f];

		foreach(i, inout vertex; vertices[1..$])
		{
			auto angle = 2.0 * PI * i / (vertices.length-1);

			vertex.position[0] = radius * cos(angle);
			vertex.position[1] = radius * sin(angle);
			vertex.position[2] = 0.0;

			vertex.texCoord[0] = 1.0;
			if(i&1)
			{
				vertex.texCoord[1] = 1.0;
			}
			else
			{
				vertex.texCoord[1] = 0.0;
			}
		}

		foreach(i, inout face; faces)
		{
			face.vertexIndices[0] = 0;
			face.vertexIndices[1] = i+1;
			if(i+2 < vertices.length)
			{
				face.vertexIndices[2] = i+2;
			}
			else
			{
				face.vertexIndices[2] = 1;
			}
		}

		return new StaticMesh(vertices, faces, texture);
	}


private:
	GGL_Mesh _mesh;
	Texture  _texture;
}



private Cache!(StaticMesh, 4<<20) _cache;
