module coneneko.billboard;
import
	coneneko.unit,
	coneneko.font,
	coneneko.math,
	coneneko.glext,
	coneneko.rgba,
	coneneko.texture,
	opengl,
	coneneko.camera,
	std.string;

/*
座標xy[-1, 1)、左下、左上、右上、右下が基本
ピクセル座標を使わない理由
windowサイズへの依存がなくなる
相対的に描画できるので、解像度の変更が容易、FSAA等の適用が楽
デメリットとして
低解像時に画面全体がぼやける
リソースのサイズ調整が面倒
等がある
*/

///
class BillBoard : Unit
{
	///
	Vector position, size, color, texCoordPosition, texCoordSize;
	
	float x() { return position.x; } ///
	float y() { return position.y; } ///
	float width() { return size.x; } ///
	float height() { return size.y; } ///
	void x(float a) { position.x = a; } ///
	void y(float a) { position.y = a; } ///
	void width(float a) { size.x = a; } ///
	void height(float a) { size.y = a; } ///
	
	///
	this(float x = -1, float y = -1, float width = 2, float height = 2, Vector color = Color.WHITE)
	{
		position = vector(x, y);
		size = vector(width, height);
		this.color = color;
		texCoordPosition = vector(0.0, 0.0);
		texCoordSize = vector(1.0, 1.0);
	}
	
	///
	bool opIn_r(Vector point)
	{
		return x < point.x && point.x < x + width
			&& y < point.y && point.y < y + height;
	}
	
	void attach()
	{
		glBegin(GL_QUADS);
		glColor4f(color.x, color.y, color.z, color.w);
		glNormal3f(0.0, 0.0, 0.0);
		
		// 左下、左上、右上、右下
		glTexCoord2f(texCoordPosition.x, texCoordPosition.y + texCoordSize.y);
		glVertex2f(position.x, position.y);
		
		glTexCoord2f(texCoordPosition.x, texCoordPosition.y);
		glVertex2f(position.x, position.y + size.y);
		
		glTexCoord2f(texCoordPosition.x + texCoordSize.x, texCoordPosition.y);
		glVertex2f(position.x + size.x, position.y + size.y);
		
		glTexCoord2f(texCoordPosition.x + texCoordSize.x, texCoordPosition.y + texCoordSize.y);
		glVertex2f(position.x + size.x, position.y);
		glEnd();
	}
	
	void detach() {}
	
	BillBoard clone() ///
	{
		auto result = new BillBoard();
		result.position = position;
		result.size = size;
		result.color = color;
		result.texCoordPosition = texCoordPosition;
		result.texCoordSize = texCoordSize;
		return result;
	}
}

///
class ImageBoard : BillBoard
{
	private Texture texture;
	
	///
	this(string fileName, float x, float y, float width, float height, Vector color = Color.WHITE)
	{
		auto rgba = new Rgba(fileName);
		this(new Texture(correctTextureSize(rgba), 0), x, y, width, height, color); // テクスチャサイズの制限はそのうち取り除く
	}
	
	private this(Texture texture, float x, float y, float width, float height, Vector color)
	{
		this.texture = texture;
		super(x, y, width, height, color);
	}
	
	ImageBoard clone() ///
	{
		return new ImageBoard(texture, x, y, width, height, color);
	}
	
	override void attach()
	{
		texture.attach();
		super.attach();
		super.detach();
		texture.detach();
	}
}

/// RenderState.blend=true が必要、textureのサイズは2のn乗(今のところ)
class TextBoard : BillBoard
{
	private const uint widthLimit, textureWidth, textureHeight;
	private Font font;
	private string _text;
	private Texture texture;
	
	///
	this(string text, float x, float y, float width = 1.2, float height = 0.1,
		Vector color = Color.WHITE, uint textureWidth = 512, uint textureHeight = 32,
		Font font = Font.getDefault())
	{
		this(0, text, x, y, width, height, color, textureWidth, textureHeight, font);
	}
	
	/// widthLimit以上で改行
	/// TODO texture size制限がなくなれば、widthLimitをtextureWidthにして、このthisは削除できる
	this(uint widthLimit, string text, float x, float y, float width, float height,
		Vector color, uint textureWidth, uint textureHeight, Font font = Font.getDefault())
	{
		super(x, y, width, height, color);
		this.widthLimit = widthLimit;
		this.textureWidth = textureWidth;
		this.textureHeight = textureHeight;
		this.font = font;
		this.text(text);
	}
	
	TextBoard clone() ///
	{
		return new TextBoard(
			widthLimit, text, x, y, width, height, color, textureWidth, textureHeight, font
		);
	}
	
	string text() ///
	{
		return _text;
	}
	
	private bool hasLimit()
	{
		return 32 <= widthLimit;
	}
	
	void text(string a) ///
	{
		if (_text == a) return;
		_text = a;
		texture = null;
		if (_text == "") return;
		
		Rgba[] array;
		if (!hasLimit)
		{
			foreach (v; _text.splitlines()) array ~= new Rgba(font, v, Color.WHITE);
		}
		else
		{
			foreach (v; _text.splitlines())
			{
				foreach (w; font.splitLimit(v, widthLimit)) array ~= new Rgba(font, w, Color.WHITE);
			}
		}
		auto rgba = array.mergeMultiLineText();
		texture = new Texture(resize(rgba, textureWidth, textureHeight), 0);
	}
	
	override void attach()
	{
		if (texture is null) return;
		texture.attach();
		super.attach();
		super.detach();
		texture.detach();
	}
}

///
class PointSprite : BillBoard
{
	/// sizeはピクセル単位
	this(Vector position, float size = 1.0, Vector color = Color.WHITE)
	{
		super(position.x, position.y, size, size, color);
		this.position.z = position.z;
	}
	
	override void attach()
	{
		with (color) glColor3f(x, y, z);
		glPointSize(size.x);
		glBegin(GL_POINTS);
		with (position) glVertex3f(x, y, z);
		glEnd();
	}
}

///
class LineRectangle : BillBoard
{
	///
	this(float x, float y, float width, float height, Vector color)
	{
		super(x, y, width, height, color);
	}
	
	override void attach()
	{
		glBegin(GL_LINE_LOOP);
		glColor4f(color.x, color.y, color.z, color.w);
		glNormal3f(0.0, 0.0, 0.0);
		glTexCoord2f(0.0, 0.0);
		glVertex2f(x, y);
		glVertex2f(x + width, y);
		glVertex2f(x + width, y + height);
		glVertex2f(x, y + height);
		glEnd();
	}
}
