module gedo.video.alphameshlist;


private import gedo.video.ggl;
private import gedo.video.color;
private import gedo.video.texture;
private import gedo.video.staticmesh;
private import gedo.video.skinnedmesh;
private import gedo.math.direction;
private import gedo.math.vector;



public class AlphaMeshList
{
public:
	this()
	{
		foreach(inout c; _containers)
		{
			c = new AlphaMesh();
		}

		_count = 0;
	}


	void clear()
	{
		_count = 0;
	}


	AlphaMesh get()
	{
		if(_count < _containers.length)
		{
			auto c = _containers[_count];

			++_count;

			return c;
		}

		return null;
	}


	void draw()
	{
		auto cs = _containers[0 .. _count];

		if(cs.length >= 2)
		{
			foreach(c; cs)
			{
				c.update();
			}

			sort(cs);
		}

		GGL_lockDepthBuffer(1);

		foreach_reverse(c; cs)
		{
			c.draw();
		}

		GGL_lockDepthBuffer(0);
		GGL_enableAlphaBlend(0);
	}


private:

	AlphaMesh[256] _containers;
	uint           _count;


	static void sort(AlphaMesh[] x)
	{
		float pivot;
		int  i;
		int  j;

		i = 0;
		j = x.length - 1;

		pivot = x[length >> 1].z;

		for(;;)
		{
			while(x[i].z < pivot)
			{
				i++;
			}

			while(pivot < x[j].z)
			{
				j--;
			}

			if (i >= j)
				break;

			{
				auto b = x[i];

				x[i] = x[j];

				x[j] = b;
			}

			i++;
			j--;
		}

		if(i > 1)
		{
			sort(x[0 .. i]);
		}
		if(j + 1 < x.length)
		{
			sort(x[j + 1 .. length]);
		}
	}
}



alias void delegate() DrawMethod;



public class AlphaMesh
{
private:
	enum Type
	{
		PARTICLE,
		STATICMESH,
		SKINNEDMESH,
		TRIANGLESTRIP,
		CUSTOM,
	}


public:
	void set(StaticMesh m, Vector p, GGL_BlendMode b, Direction d = null)
	{
		_type = Type.STATICMESH;

		_position[] = p[];
		_direction  = d;
		_blendMode  = b;

		_uni.staticMesh  = m;
	}


	void set(SkinnedMesh m, Vector p, uint i, uint f, GGL_BlendMode b, Direction d = null)
	{
		_type = Type.SKINNEDMESH;

		_position[] = p[];
		_direction  = d;
		_blendMode  = b;

		_uni.skinnedMesh = m;
		_uni.index       = i;
		_uni.frame       = f;
	}


	void set(GGL_Vertex[] vertices, Texture tex, Vector p, Direction d, GGL_BlendMode b)
	{
		_type = Type.TRIANGLESTRIP;

		_position[] = p[];
		_direction  = d;
		_blendMode  = b;

		_uni.vertices  = vertices;
		_uni.texture   = tex;
	}


	void set(GGL_Vertex[] vertices, Texture tex, GGL_BlendMode b)
	{
		static Direction _noRotationDirection = null;
		if(!_noRotationDirection)
		{
			_noRotationDirection = new Direction();
		}

		_type = Type.TRIANGLESTRIP;

		_position[] = 0.0;
		_direction  = _noRotationDirection;
		_blendMode  = b;

		_uni.vertices  = vertices;
		_uni.texture   = tex;
	}


	void set(Texture tex, float sx, float sy, Vector p, GGL_BlendMode b)
	{
		_type = Type.PARTICLE;

		_position[] = p[];
		_direction  = null;
		_blendMode  = b;

		_uni.particle.texture = tex;
		_uni.particle.size[0] = sx;
		_uni.particle.size[1] = sy;
	}


	void set(Vector p, DrawMethod m)
	{
		_type = Type.CUSTOM;

		_position[] = p[];
		_direction  = null;
		_blendMode  = GGL_BlendMode.MIX;

		_uni.draw   = m;
	}


	void update()
	{
		_z = GGL_getDepth(_position[0], _position[1], _position[2]);
	}


	void draw()
	{
		GGL_pushMatrix();

		if(_type == Type.CUSTOM)
		{
			_uni.draw();
		}
		else
		{
			if(_direction)
			{
				_direction.transform(_position);
			}
			else
			{
				translate(_position);

				GGL_enterBillBoardMode();
			}

			GGL_setBlendMode(_blendMode);
			switch(_type)
			{
			case Type.STATICMESH:
				_uni.staticMesh.draw();
				break;

			case Type.SKINNEDMESH:
				_uni.skinnedMesh.draw(_uni.index, _uni.frame);
				break;

			case Type.TRIANGLESTRIP:
				_uni.texture.draw(_uni.vertices);
				break;

			case Type.PARTICLE:
				_uni.particle.texture.draw(_uni.particle.size[0], _uni.particle.size[1]);
				break;
			}
		}

		GGL_popMatrix();
	}


	float z()
	{
		return _z;
	}


private:
	Type          _type;
	float         _z;
	Vector        _position;
	Direction     _direction;
	GGL_BlendMode _blendMode;

	union Uni
	{
		StaticMesh staticMesh;

		struct
		{
			SkinnedMesh skinnedMesh;
			uint        frame;
			uint        index;
		}

		struct
		{
			GGL_Vertex[] vertices;
			Texture      texture;
		}

		DrawMethod draw;
		Particle   particle;
	}

	Uni _uni;

	struct Particle
	{
		float[2] size;
		Texture  texture;
	}
}

