module y4d_draw.transbltter;

private import y4d_draw.drawbase;
private import y4d_draw.texturebase;
private import y4d_draw.screen;
private import std.math;

///	転送時のbltter
/**
	転送時のbltter。yaneSDK2ndのCPlaneTransBltっぽいの。
	ノベルゲームとかでシーン切り替えに使う。<BR>

	このモジュールは大きいので、import y4d;では読み込まないので
	このファイルだけimportしてください：<BR>

		private import y4d_draw.transblitter;<BR>

	phase = 0 ～ 255。255で転送完了するものとする。
	内部状態は持っていない。

*/
class TransBltter {

///	左からカーテン。16 dot。
static void bltSlitCurtain1(Screen dst,ITextureBase src,int x,int y,int phase)
{ bltTransHelper1(dst,src,x,y,phase,true,16); }

///	右からカーテン。16 dot。
static void bltSlitCurtain2(Screen dst,ITextureBase src,int x,int y,int phase)
{ bltTransHelper1(dst,src,x,y,phase,false,16); }

///	左からカーテン。8 dot。
static void bltSlitCurtain3(Screen dst,ITextureBase src,int x,int y,int phase)
{ bltTransHelper1(dst,src,x,y,phase,true,8); }

///	右からカーテン。8 dot。
static void bltSlitCurtain4(Screen dst,ITextureBase src,int x,int y,int phase)
{ bltTransHelper1(dst,src,x,y,phase,false,8); }

///	左からカーテン（FadeBlt）。128 dot。
static void bltSlitCurtain9(Screen dst,ITextureBase src,int x,int y,int phase)
{ bltTransHelper10(dst,src,x,y,phase,true,128); }

///	右からカーテン（FadeBlt）。128 dot。
static void bltSlitCurtain10(Screen dst,ITextureBase src,int x,int y,int phase)
{ bltTransHelper10(dst,src,x,y,phase,false,128); }

///	上からカーテン（FadeBlt）。128 dot。
static void bltSlitCurtain11(Screen dst,ITextureBase src,int x,int y,int phase)
{ bltTransHelper11(dst,src,x,y,phase,true,128); }

///	下からカーテン（FadeBlt）。128 dot。
static void bltSlitCurtain12(Screen dst,ITextureBase src,int x,int y,int phase)
{ bltTransHelper11(dst,src,x,y,phase,false,128); }

///	上からのカーテン。16 dot。
static void bltSlitCurtain5(Screen dst,ITextureBase src,int x,int y,int phase)
{ bltTransHelper2(dst,src,x,y,phase,true,16); }

///	下からのカーテン。16 dot。
static void bltSlitCurtain6(Screen dst,ITextureBase src,int x,int y,int phase)
{ bltTransHelper2(dst,src,x,y,phase,false,16); }

///	上からのカーテン。8 dot。
static void bltSlitCurtain7(Screen dst,ITextureBase src,int x,int y,int phase)
{ bltTransHelper2(dst,src,x,y,phase,true,8); }

///	下からのカーテン。8 dot。
static void bltSlitCurtain8(Screen dst,ITextureBase src,int x,int y,int phase)
{ bltTransHelper2(dst,src,x,y,phase,false,8); }

///	上からの円
static void bltCircle1(Screen dst,ITextureBase src,int x,int y,int phase)
{
	if (!dst || !src) return ;
	int sx = cast(int)src.getWidth(); int sy = cast(int)src.getHeight();

	int sm;	// max size
	sm = cast(int)(std.math.sqrt(cast(float)sx*sx/4+sy*sy)*2);
	int sr;	// rest size
	sr = (sm * phase) >> 8;
	int ssy,py;
	ssy = 0;
	for(py=0;py<(sr>>1);py++,ssy++){
		int px,rx;
		rx = cast(int)(std.math.sqrt(cast(float)sr*sr/4-ssy*ssy)*2);
		px = (sx>>1)-(rx>>1);

//		bltRect(px,py,rx,1); // クリップ付きHLINE
		//	手動マクロ

		//	bltRect(x_,y_,sx_,sy_)
		int x_ = px , y_ = py , sx_=rx , sy_ = 1;

		{	//	マクロだと思いねぇ
		Rect rc;
		int	sx2,sy2;
		sx2= sx_; sy2= sy_;
		int x2,y2;
		x2 = x_; y2= y_;
		if(x2<0) { x2=0; }
		if(y2<0) { y2=0; }
		if(x2+sx2 > sx) { sx2 = sx-x2; }
		if(y2+sy2 > sy) { sy2 = sy-y2; }
		rc.setRect(x2,y2,x2+sx2,y2+sy2);
		dst.blt(src,x+x2,y+y2,&rc);
		}
	}
}

///	下からの円
static void bltCircle2(Screen dst,ITextureBase src,int x,int y,int phase)
{
	if (!dst || !src) return ;
	int sx = cast(int)src.getWidth(); int sy = cast(int)src.getHeight();

	int sm;	// max size
	sm = cast(int)(std.math.sqrt(cast(float)sx*sx/4+sy*sy)*2);
	int sr;	// rest size
	sr = (sm * phase) >> 8;
	int ssy,py;
	ssy = 0;
	for(py=sy;py>sy-(sr>>1);py--,ssy++){
		int px,rx;
		rx = cast(int)(std.math.sqrt(cast(float)sr*sr/4-ssy*ssy)*2);
		px = (sx>>1)-(rx>>1);

//		bltRect(px,py,rx,1); // クリップ付きHLINE
		//	手動マクロ

		//	bltRect(x_,y_,sx_,sy_)
		int x_ = px , y_ = py , sx_=rx , sy_ = 1;

		{	//	マクロだと思いねぇ
		Rect rc;
		int	sx2,sy2;
		sx2= sx_; sy2= sy_;
		int x2,y2;
		x2 = x_; y2= y_;
		if(x2<0) { x2=0; }
		if(y2<0) { y2=0; }
		if(x2+sx2 > sx) { sx2 = sx-x2; }
		if(y2+sy2 > sy) { sy2 = sy-y2; }
		rc.setRect(x2,y2,x2+sx2,y2+sy2);
		dst.blt(src,x+x2,y+y2,&rc);
		}
	}
}

///	左からの円
static void bltCircle3(Screen dst,ITextureBase src,int x,int y,int phase)
{
	if (!dst || !src) return ;
	int sx = cast(int)src.getWidth(); int sy = cast(int)src.getHeight();

	int sm;	// max size
	sm = cast(int)(std.math.sqrt(cast(float)sx*sx+sy*sy/4)*2);
	int sr;	// rest size
	sr = (sm * phase) >> 8;
	int ssy,py;
	ssy =-(sr >> 1);
	for(py=(sy>>1)-(sr>>1);py<((sy>>1)+(sr>>1));py++,ssy++){
		int px,rx;
		rx = cast(int)(std.math.sqrt(cast(float)sr*sr/4-ssy*ssy));
		px = 0;

//		bltRect(px,py,rx,1); // クリップ付きHLINE
		//	手動マクロ

		//	bltRect(x_,y_,sx_,sy_)
		int x_ = px , y_ = py , sx_=rx , sy_ = 1;

		{	//	マクロだと思いねぇ
		Rect rc;
		int	sx2,sy2;
		sx2= sx_; sy2= sy_;
		int x2,y2;
		x2 = x_; y2= y_;
		if(x2<0) { x2=0; }
		if(y2<0) { y2=0; }
		if(x2+sx2 > sx) { sx2 = sx-x2; }
		if(y2+sy2 > sy) { sy2 = sy-y2; }
		rc.setRect(x2,y2,x2+sx2,y2+sy2);
		dst.blt(src,x+x2,y+y2,&rc);
		}
	}
}

///	右からの円
static void bltCircle4(Screen dst,ITextureBase src,int x,int y,int phase)
{
	if (!dst || !src) return ;
	int sx = cast(int)src.getWidth(); int sy = cast(int)src.getHeight();

	int sm;	// max size
	sm = cast(int)(std.math.sqrt(cast(float)sx*sx+sy*sy/4)*2);
	int sr;	// rest size
	sr = (sm * phase) >> 8;
	int ssy,py;
	ssy =-(sr >> 1);
	for(py=(sy>>1)-(sr>>1);py<((sy>>1)+(sr>>1));py++,ssy++){
		int px,rx;
		rx = cast(int)(std.math.sqrt(cast(float)sr*sr/4-ssy*ssy));
		px = sx-rx;

//		bltRect(px,py,rx,1); // クリップ付きHLINE
		//	手動マクロ

		//	bltRect(x_,y_,sx_,sy_)
		int x_ = px , y_ = py , sx_=rx , sy_ = 1;

		{	//	マクロだと思いねぇ
		Rect rc;
		int	sx2,sy2;
		sx2= sx_; sy2= sy_;
		int x2,y2;
		x2 = x_; y2= y_;
		if(x2<0) { x2=0; }
		if(y2<0) { y2=0; }
		if(x2+sx2 > sx) { sx2 = sx-x2; }
		if(y2+sy2 > sy) { sy2 = sy-y2; }
		rc.setRect(x2,y2,x2+sx2,y2+sy2);
		dst.blt(src,x+x2,y+y2,&rc);
		}
	}
}

///	中央からの円
static void bltCircle5(Screen dst,ITextureBase src,int x,int y,int phase)
{
	if (!dst || !src) return ;
	int sx = cast(int)src.getWidth(); int sy = cast(int)src.getHeight();

	int sm;	// max size
	sm = sx+sy;
	int sr;	// rest size
	sr = (sm * phase) >> 8;
	int ssy,py;
	ssy =-(sr >> 1);
	for(py=(sy>>1)-(sr>>1);py<((sy>>1)+(sr>>1));py++,ssy++){
		int px,rx;
		rx = cast(int)(std.math.sqrt(cast(float)sr*sr/4-ssy*ssy)*2); // bug-fixed '00/02/24
		px = (sx>>1)-(rx>>1);

//		bltRect(px,py,rx,1); // クリップ付きHLINE
		//	手動マクロ

		//	bltRect(x_,y_,sx_,sy_)
		int x_ = px , y_ = py , sx_=rx , sy_ = 1;

		{	//	マクロだと思いねぇ
		Rect rc;
		int	sx2,sy2;
		sx2= sx_; sy2= sy_;
		int x2,y2;
		x2 = x_; y2= y_;
		if(x2<0) { x2=0; }
		if(y2<0) { y2=0; }
		if(x2+sx2 > sx) { sx2 = sx-x2; }
		if(y2+sy2 > sy) { sy2 = sy-y2; }
		rc.setRect(x2,y2,x2+sx2,y2+sy2);
		dst.blt(src,x+x2,y+y2,&rc);
		}
	}
}

///	うずまき 外から 2*2
static void bltWhorl1(Screen dst,ITextureBase src,int x,int y,int phase)
{
	static int uzu[16] = [
		 0, 1, 2, 3,
		11,12,13, 4,
		10,15,14, 5,
		 9, 8, 7, 6
	];
	bltTransHelper3(dst,src,x,y,phase,2,uzu);
}

///	うずまき 内から 2*2
static void bltWhorl2(Screen dst,ITextureBase src,int x,int y,int phase)
{
	static int uzu[16] = [
		 6, 7, 8, 9,
		 5, 0, 1,10,
		 4, 3, 2,11,
		15,14,13,12
	];
	bltTransHelper3(dst,src,x,y,phase,2,uzu);
}

///	うずまき 外から 4*4
static void bltWhorl3(Screen dst,ITextureBase src,int x,int y,int phase)
{
	static int uzu[16] = [
		 0, 1, 2, 3,
		11,12,13, 4,
		10,15,14, 5,
		 9, 8, 7, 6
	];
	bltTransHelper3(dst,src,x,y,phase,4,uzu);
}

///	うずまき 内から 4*4
static void bltWhorl4(Screen dst,ITextureBase src,int x,int y,int phase)
{
	static int uzu[16] = [
		 6, 7, 8, 9,
		 5, 0, 1,10,
		 4, 3, 2,11,
		15,14,13,12
	];
	bltTransHelper3(dst,src,x,y,phase,4,uzu);
}

///	うずまき 外から 8*8
static void bltWhorl5(Screen dst,ITextureBase src,int x,int y,int phase)
{
	static int uzu[16] = [
		 0, 1, 2, 3,
		11,12,13, 4,
		10,15,14, 5,
		 9, 8, 7, 6
	];
	bltTransHelper3(dst,src,x,y,phase,8,uzu);
}

///	うずまき 内から 8*8
static void bltWhorl6(Screen dst,ITextureBase src,int x,int y,int phase)
{
	static int uzu[16] = [
		 6, 7, 8, 9,
		 5, 0, 1,10,
		 4, 3, 2,11,
		15,14,13,12
	];
	bltTransHelper3(dst,src,x,y,phase,8,uzu);
}

// --------------- 以下はhelper -------------------------------------------

/// 左右からのカーテン。
/**
	SlitCurtainBltから呼び出されるヘルパ関数。
	bLeft == trueならば左から。falseなら右からのカーテン。
*/
static void bltTransHelper1(Screen dst,ITextureBase src,int x,int y,int phase,bool bLeft,int width){
	if (!dst || !src) return ;
	int sx = cast(int)src.getWidth(); int sy = cast(int)src.getHeight();

	int ColumnNum = (sx + width + 1) / width;
	int c = (phase * (ColumnNum + width)) / 256;
	int i, j;
	Rect rc;

	rc.top = 0;
	rc.bottom = sy;
	for(i = 0; i < ColumnNum && i < c; ++i){
		j = c - i;
		if(j > width)
			j = width;
		rc.left = i * width;
		if(bLeft){
			rc.right = rc.left + j;
		}else{
			rc.right = sx - rc.left;
			rc.left = rc.right - j;
		}
		if(rc.right > sx)
			rc.right = sx - 1;
		if(rc.left < 0)
			rc.left = 0;

		dst.blt(src,cast(int)(x + rc.left), y, &rc);
	}
}

/// 上下のカーテン。
/**
	SlitCurtainBltから呼び出されるヘルパ関数。
	bTop == trueならば上から。falseなら下からのカーテン。
*/
static void bltTransHelper2(Screen dst,ITextureBase src,int x,int y,int phase,bool bTop,int height){

	if (!dst || !src) return ;
	int sx = cast(int)src.getWidth(); int sy = cast(int)src.getHeight();

	int ColumnNum = (sy + height + 1) / height;
	int c = (phase * (ColumnNum + height)) / 256;
	int i, j;
	Rect rc;

	rc.left = 0;
	rc.right = sx;
	for(i = 0; i < ColumnNum && i < c; ++i){
		j = c - i;
		if(j > height)
			j = height;
		rc.top = i * height;
		if(bTop){
			rc.bottom = rc.top + j;
		}else{
			rc.bottom = sy - rc.top;
			rc.top = rc.bottom - j;
		}
		if(rc.bottom > sy)
			rc.bottom = sy - 1;
		if(rc.top < 0)
			rc.top = 0;

		dst.blt(src,x, cast(int)(y + rc.top),&rc);
	}
}

///	うずまきetc..
/**
	uzuは4*4の配列。使いかたはbltWhorl1を参考にすること。
*/
static void bltTransHelper3(Screen dst,ITextureBase src,int x,int y,int phase,int a,int uzu[16]){
	if (!dst || !src) return ;
	int sx = cast(int)src.getWidth(); int sy = cast(int)src.getHeight();

	phase >>= 4;
	for(int py=0,py2=0;py<sy;py+=a,++py2){
		for(int px=0,px2=0;px<sx;px+=a,++px2){
			if (uzu[(px2 & 3)+((py2 & 3)<<2) & 15] <= phase){

//		bltRect(px,py,a,a); // クリップ付きHLINE
		//	手動マクロ

		//	bltRect(x_,y_,sx_,sy_)
		int x_ = px , y_ = py , sx_=a , sy_ = a;

		{	//	マクロだと思いねぇ
		Rect rc;
		int	sx2,sy2;
		sx2= sx_; sy2= sy_;
		int x2,y2;
		x2 = x_; y2= y_;
		if(x2<0) { x2=0; }
		if(y2<0) { y2=0; }
		if(x2+sx2 > sx) { sx2 = sx-x2; }
		if(y2+sy2 > sy) { sy2 = sy-y2; }
		rc.setRect(x2,y2,x2+sx2,y2+sy2);
		dst.blt(src,x+x2,y+y2,&rc);
		}

			}
		}
	}
}

/// 左右からのカーテン（FadeBltter）。
/**
	SlitCurtainBltから呼び出されるヘルパ関数。
	bLeft == trueならば左から。falseなら右からのカーテン。
*/

static void bltTransHelper10(Screen dst,ITextureBase src,int x,int y,int phase,bool bLeft,int width){
	if (!dst || !src) return ;

	if ( phase == 0 ) return;
	
	if ( phase == 255 ) {
		dst.blt(src, x, y);
		return;
	}

	Color4ub color = dst.getColor4ub();
	int sx = cast(int) src.getWidth(); 
	int sy = cast(int) src.getHeight();
	
	int i, j;
	float alpha = 0.0f;
	// 現在のphase値における描画確定位置の端を求める
	int c = cast(int) (((sx+width)/255.0) * phase);
	float aAlpha = 255.0 / cast(float) width;
	Rect rc;

	rc.top = 0;
	rc.bottom = sy;
	if (bLeft) {
		rc.left = 0;
		rc.right = c - width;
	} else {
		rc.left = sx - c + width;
		rc.right = sx;
	}
	
	// 255 部分描画
	dst.setColor(color.r,color.b,color.b,255);
	if ( rc.left < rc.right) {
		dst.blt(src,cast(int)(x + rc.left), y, &rc);
	}
	
	// fade部分描画
	for(i=0,alpha=255.0; i<width; ++i,alpha-=aAlpha) {
		
		if (bLeft) {
			rc.left = rc.right;
			rc.right = rc.left + 1;
			
			if (rc.right > sx) break;
		} else {
			rc.right = rc.left;
			rc.left = rc.right - 1;
			
			if (rc.left < 0) break;
		}
		dst.setColor(color.r,color.b,color.b,cast(int) alpha);
		dst.blt(src,cast(int)(x+rc.left), y, &rc);
	}
	
	dst.setColor(color);
}

/// 上下からのカーテン（FadeBltter）。
/**
	SlitCurtainBltから呼び出されるヘルパ関数。
	bLeft == trueならば左から。falseなら右からのカーテン。
*/

static void bltTransHelper11(Screen dst,ITextureBase src,int x,int y,int phase,bool bTop,int height){
	if (!dst || !src) return ;

	if ( phase == 0 ) return;
	
	if ( phase == 255 ) {
		dst.blt(src, x, y);
		return;
	}

	Color4ub colorOrg = dst.getColor4ub();
	int sx = cast(int) src.getWidth(); 
	int sy = cast(int) src.getHeight();

	int i, j;
	float alpha;
	int c = cast(int) ( ((sy+height)/255.0) * phase);
	float aAlpha = 255.0 / cast(float) height;
	Rect rc;

	rc.left = 0;
	rc.right = sx;
	if (bTop) {
		rc.top = 0;
		rc.bottom = c - height;
	} else {
		rc.top = sy - c + height;
		rc.bottom = sy;
	}
	
	dst.setColor(colorOrg.r,colorOrg.b,colorOrg.b,255);
	if ( rc.top <= rc.bottom) {
		dst.blt(src,x, cast(int)(y + rc.top), &rc);
	}
	for(i=0,alpha=255.0; i<height; ++i,alpha-=aAlpha) {
		
		if (bTop) {
			rc.top = rc.bottom;
			rc.bottom = rc.top + 1;
			
			if (rc.bottom > sy) break;
		} else {
			rc.bottom = rc.top;
			rc.top = rc.bottom - 1;
			if (rc.top < 0) break;
		}
		dst.setColor(colorOrg.r,colorOrg.g,colorOrg.b, cast(int) alpha);
		dst.blt(src,x, cast(int)(y + rc.top), &rc);
	}
	dst.setColor(colorOrg);

}

/// 単純フェードアウト描画（互換性の為に導入
static void bltFadeBlt(Screen dst,ITextureBase src,int x,int y,int phase) {
	if ( phase == 0 ) return;
	
	Color4ub color = dst.getColor4ub();
	dst.setColor(color.r, color.g, color.b, phase);
	dst.blt(src, x, y);
	dst.setColor(color);
}


/// 番号で呼び出せる
static int blt(int no,Screen dst,ITextureBase src,int x,int y,int phase) {	//	番号で呼び出せるのだ
	
	alias void function(Screen,ITextureBase,int,int,int) TransFunc;
	
	static TransFunc[] transfunc= [
		&TransBltter.bltSlitCurtain1,	// 0
		&TransBltter.bltSlitCurtain2,	// 1
		&TransBltter.bltSlitCurtain3,	// 2
		&TransBltter.bltSlitCurtain4,	// 3
		&TransBltter.bltSlitCurtain5,	// 4
		&TransBltter.bltSlitCurtain6,	// 5
		&TransBltter.bltSlitCurtain7,	// 6
		&TransBltter.bltSlitCurtain8,	// 7
		&TransBltter.bltCircle1,		// 8
		&TransBltter.bltCircle2,		// 9
		&TransBltter.bltCircle3,		// 10
		&TransBltter.bltCircle4,		// 11
		&TransBltter.bltCircle5,		// 12
		&TransBltter.bltWhorl1,			// 13
		&TransBltter.bltWhorl2,			// 14
		&TransBltter.bltWhorl3,			// 15
		&TransBltter.bltWhorl4,			// 16
		&TransBltter.bltWhorl5,			// 17
		&TransBltter.bltWhorl6,			// 18
		&TransBltter.bltFadeBlt,		// 19
		&TransBltter.bltSlitCurtain9,	// 20
		&TransBltter.bltSlitCurtain10,	// 21
		&TransBltter.bltSlitCurtain11,	// 22
		&TransBltter.bltSlitCurtain12,	// 23
	];

	if (no >= transfunc.length) return 1; // 範囲外

	transfunc[no](dst,src,x,y,phase);
	
	return 0;
}


}
