module y4d_draw.drawcontext;

private import y4d_draw.drawbase;
private import opengl;

///	描画用のContext
/**
	矩形でのclippingの実現のために、openGLの
		glViewport(x1,screenSizeY-y2,x2-x1,y2-y1);
		glOrtho(x1,x2,y2,y1,0,256);
	を行なっている箇所があるので、これがまずければ、このDrawContext依存系の
	描画は行なわないこと。
*/
class DrawContext {

	///	スクリーンのオフセット値を設定する
	/**
		Screen.setOffsetで設定される値は、視体積を変更するので
		クリップとは関係ないが、ここで変更されるoffsetは直接的に
		描画に関係してくる。
		offsetX = 1.0 ならば、一画面分(rateX)描画時にオフセットが加わる。
		すなわち、(-640,-480)に描画したものが(0,0)に描画される
	*/
	void	setOffset(float ox,float oy)
	{
		offsetX = ox;
		offsetY = oy;
		update();
	}

	///	スクリーンのオフセット値を取得する
	void	getOffset(out float ox,out float oy)
	{
		ox = offsetX;
		oy = offsetY;
	}

	///	クローンメソッド
	/**
		データメンバのcopyは行なわない。
		あくまで同じ型のオブジェクトを生成するだけ。
	*/
	DrawContext clone() { return new DrawContext; }

	///	画面サイズの設定
	/**
		(screenSizeX,screenSizeY)にそのまま反映します
	*/
	void	setScreenSize(float x,float y){
		screenSizeX = x;
		screenSizeY = y;
		update();
	}

	///	画面サイズの取得
	void	getScreenSize(out float x,out float y){
		x = screenSizeX;
		y = screenSizeY;
	}

	///	クリップする矩形を指定する
	/**
		ここで指定するRectの座標系は、
		(screenSizeX,screenSizeY)を基準とする。<BR>

		Rectの(left,bottm)の点は含まない。<BR>

		すなわち、 screenSizeX = 640, screenSize Y = 480 のときに
		(0,0,640,480)のRectをClip矩形として渡した場合、
		top = 0, left = 0, right = 1.0 , bottom = 1.0
		がclip位置として記録されることになる。

		rc = nullの場合、(0,0,1.0,1.0)が設定される。
	*/
	void	setClipRect(Rect*rc) {
		if (rc) {
			left   = rc.left   / screenSizeX;
			top    = rc.top    / screenSizeY;
			right  = rc.right  / screenSizeX;
			bottom = rc.bottom / screenSizeY;
		} else {
			left = top = 0.0;
			right = bottom = 1.0;
		}
		update();
	}

	///	クリップを有効にする
	void	enableClip() { bClip = true; update(); }

	///	クリップを無効にする(default)
	void	disableClip() { bClip = false; update(); }

	///	クリップが有効かどうかを取得する
	bool	isClip() { return bClip; }

	///	描画時の倍率の設定
	/**
		640×480の画面を想定してコーディングしたものを
		320×240の画面にうまく描画されるようにするためには
		setRate(0.5,0.5)を行なえば良い。<BR>

		update での計算式を見ればわかるように、
		offset , clipRect に関係してくる。<BR>

		defaultでは(1.0,1.0)
	*/
	void	setRate(float rateX_,float rateY_) {
		rateX = rateX_;
		rateY = rateY_;
	}

	///	描画時の倍率の取得
	void	getRate(out float rateX_,out float rateY_) {
		rateX_ = rateX;
		rateY_ = rateY;
	}

	/// update
	/**
		1.offsetの値を反映させる<BR>
		offsetRX = offsetX * screenSizeX * rateX;
		offsetRY = offsetY * screenSizeY * rateY; <BR>

		2.rectの値を反映させる<BR>
<PRE>
		leftR = left * screenSizeX * rateX;
		topR  = top  * screenSizeY * rateY;
		rightR= right * screenSizeX * rateX;
		bottomR= bottom * screenSizeY * rateY;
</PRE>
	*/
	void	update() {
		float rx = screenSizeX * rateX;
		float ry = screenSizeY * rateY;

		offsetRX = offsetX * rx;
		offsetRY = offsetY * ry;

		leftR = (left + offsetX) * rx;
		topR  = (top  + offsetY) * ry;
		rightR= (right + offsetX)  * rx;
		bottomR=(bottom + offsetY) * ry;

		if (bClip) {
			glLoadIdentity();
			int x1 = cast(int)leftR , x2 = cast(int)rightR , y1 = cast(int)topR , y2 = cast(int)bottomR ;
			if (x1>x2) x2=x1;
			if (y1>y2) y2=y1;
			// orthoは整数なので、viewportに渡すのと丸めかたが違うと誤差が出る
			glViewport(x1,cast(int)(screenSizeY-y2),x2-x1,y2-y1);
			glOrtho(x1,x2,y2,y1,0,256);
		} else {
			glLoadIdentity();
			glViewport(0,0,cast(int)screenSizeX,cast(int)screenSizeY);
			glOrtho(0,cast(int)screenSizeX,cast(int)screenSizeY,0,0,256);
		}

	}

	this() {
		screenSizeX = screenSizeY = 1.0;
		rateX = rateY = 1.0;
		offsetX = offsetY = 0.0;
		update();
	}

//---- 以下、メンバ。場合によっては直接アクセスしても構わない。

	float left;	  //!< 描画するときのclip情報 左
	float top;	  //!< 描画するときのclip情報 上
	float right;  //!< 描画するときのclip情報 右
	float bottom; //!< 描画するときのclip情報 下

	bool  bClip; //!< clipするときはこれがtrue

	float screenSizeX;	//!< 640×480ならば640
	float screenSizeY;	//!< 640×480ならば480

	float offsetX,offsetY;
	//!<	スクリーンのオフセット値

	float offsetRX,offsetRY;
	//!<  offsetRX = offsetX * screenSizeX , offsetRY = offsetY * screenSizeY
	//	↑この値使うと話が早いな

	float leftR,topR,rightR,bottomR;
	//!<  rateとscreenSize,offsetを反映させたもの。実clip座標
	//	↑この値使うと話が早いな

	float rateX,rateY;
	//!<	描画時の倍率
}
