module yamalib.draw.conbinetexture;

private import std.math;

private import y4d_draw.screen;
private import y4d_draw.texture;
private import y4d_math.sintable;

private import yamalib.log.log;
/** 
	複数のテクスチャを連結し、仮想的に大きなテクスチャを
	再現します 
*/
class ConbineTexture {
	
	
	/// 2次元配列のテクスチャをもとに、一枚のテクスチャと見なす
	void setTextureMatrix(Texture[][] mat) {
		texMat = mat;
	}
	
	/// 描画処理	
	void onDraw(Screen screen, int x, int y) {
		if (texMat is null) return;
		
		int h=0,w=0;
		foreach ( inout Texture[] texs; texMat ) {
			foreach ( inout Texture t; texs ) {
				
				// 描画
				screen.blt( t, x + w, y + h );
				
				w += cast(int) t.getWidth();
			}
			w = 0;
			h += cast(int) texs[0].getHeight();
		}
		
	}
	
	/// 縮小拡大、回転ありバージョン x,yは画像の中心が描画される位置固定...
	void onDrawRotate(Screen screen, int x, int y, int rad, float rate) {
		
		if ( rate <= 0.0f ) return;
		
		bool rot = cast(bool) ((rad % 512) != 0);
		uint baseRad;

		static bool bMod = false;
		static float rr = 0.0f;
		if (rr == rate) {
			bMod = false;
		} else {
			bMod = true;
		}
		rr = rate;

		float w = getWidth() * rate;
		float h = getHeight() * rate;
		
		// 拡縮後の画像サイズより描画開始位置を計算
		int startX = cast(int) (x - (w/2));
		int startY = cast(int) (y - (h/2));
		
		// 残念な呼び出しされている...
		if ( 1.0f == rate && !rot ) {
			onDraw(screen, startX, startY);
			return;
		}
		
		int r = cast(int) dist(startX, startY, x, y );

		if ( rot ) {
			baseRad = 512 - (sin.atan( -cast(int)(w/2), cast(int)(h/2) ) >> 7);
			startX = x + sin.cos( baseRad + rad, r );
			startY = y + sin.sin( baseRad + rad, r );
		}
		
		float dh=0,dw=0;
		int sh = 0;
		int ad = 0;
		bool fst = true;
		int cnt;
		float tmpF;
		foreach (inout Texture[] texs; texMat) {
			foreach (inout Texture t; texs) {
				screen.bltRotate( t, cast(int) (startX + dw), 
										cast(int) (startY + dh), rad, rate, 0 );
				
				if ( rot ) {
					dw += sin.cos( rad, cast(int) (t.getWidth() * rate) );
					dh += sin.sin( rad, cast(int) (t.getWidth() * rate) );
				} else {
					dw += cast(int) (t.getWidth() * rate);
				}
//if (bMod)				
//printf("sx %d, sy%d  dw %d, dh %d, cnt %d \n ", startX, startY, dw, dh, ++cnt);
			} 
			fst = false;
			if ( rot ) {
				sh = cast(int) (texs[0].getHeight() * rate);
				
				startX -= sin.sin(rad, sh);
				startY += sin.cos(rad, sh);
				
				dw = 0.0f;
				dh = 0.0f;
			} else {
				dw = 0.0f;
				dh += cast(int) ( texs[0].getHeight() * rate );
			}
		}
		
	}
	
	static int LC_SIZE = 512;
	
	/// 縮小拡大、回転ありバージョン x,yは画像の中心が描画される位置固定...
	void onDrawRotateFix(Screen screen, int x, int y, int rad, float rate) {
		
		if ( rate <= 0.0f ) return;
		
		bool rot = cast(bool) ((rad % 512) != 0);
		uint baseRad;

		// 残念な呼び出しされている...
		if ( 1.0f == rate && !rot ) {
//			return;
		}
		
		int startX;
		int startY;
		int sh = 0;
		int ad = 0;
		bool fst = true;
		float tmpF;
		
		int ix,iy;
		Texture curTex;
		int tpx = cast(int) ((x % LC_SIZE) * rate);
		int tpy = cast(int) ((y % LC_SIZE) * rate);
		int dh=0,dw=0;
		int sxHalf = screen.getWidth()/2;
		int syHalf = screen.getHeight()/2;
		
		ix = x / LC_SIZE;
		iy = y / LC_SIZE;
		
		
		// 今描画したい点を基点として周囲の情報描画する
		startX = sxHalf - tpx;
		startY = syHalf - tpy;

		// 現在の対象画像の描画開始点までの距離を求める
		int r = cast(int) dist(startX, startY, sxHalf, syHalf );
		if ( rot ) {
			baseRad = 512 - (sin.atan( cast(int)(startX-sxHalf), cast(int) (syHalf-startY) ) >> 7);
			startX = sxHalf + sin.cos( baseRad + rad, r );
			startY = syHalf + sin.sin( baseRad + rad, r );
		}

		// 左上に遡って描画
		for ( iy = y / LC_SIZE; iy >= 0; --iy) {
			for (ix = x / LC_SIZE; ix >= 0; --ix) {
				curTex = texMat[iy][ix];
				screen.bltRotate( curTex, cast(int) (startX+dw), cast(int)(startY+dh), 
										rad, rate, 0 );
				if (rot) {
					dw -= sin.cos( rad, cast(int) (curTex.getWidth() * rate) );
					dh -= sin.sin( rad, cast(int) (curTex.getWidth() * rate) );
				} else {
					dw -= curTex.getWidth() * rate;
				}
			}
			if (rot) {
				sh = cast(int) (curTex.getHeight() * rate);
				
				startX += sin.sin(rad, sh);
				startY -= sin.cos(rad, sh);
				
				dw = 0;
				dh = 0;
			} else {
				dw = 0;
				dh -= curTex.getHeight() * rate;
			}
		}
		
		startX = sxHalf - tpx;
		startY = syHalf - tpy;
		startX += curTex.getWidth() * rate;
		dw = 0;
		dh = 0;
		
		r = cast(int) dist(startX, startY, sxHalf, syHalf );
		if ( rot ) {
			baseRad = 512 - (sin.atan( cast(int)(startX-sxHalf), cast(int) (syHalf-startY) ) >> 7);
			startX = sxHalf + sin.cos( baseRad + rad, r );
			startY = syHalf + sin.sin( baseRad + rad, r );
		}

		// 右上に遡って描画
		for ( iy = y / LC_SIZE; iy >= 0; --iy) {
			for (ix = x / LC_SIZE + 1; ix < texMat[iy].length; ++ix) {
				curTex = texMat[iy][ix];
				screen.bltRotate( curTex, cast(int) (startX+dw), cast(int)(startY+dh), 
										rad, rate, 0 );
				if (rot) {
					dw += sin.cos( rad, cast(int) (curTex.getWidth() * rate) );
					dh += sin.sin( rad, cast(int) (curTex.getWidth() * rate) );
				} else {
					dw += curTex.getWidth() * rate;
				}
			}
			if (rot) {
				sh = cast(int) (curTex.getHeight() * rate);
				
				startX += sin.sin(rad, sh);
				startY -= sin.cos(rad, sh);
				
				dw = 0;
				dh = 0;
			} else {
				dw = 0;
				dh -= curTex.getHeight() * rate;
			}
		}
		
		startX = sxHalf - tpx;
		startY = syHalf - tpy;
		startX += LC_SIZE * rate;
		startY += LC_SIZE * rate;
		dw = 0;
		dh = 0;
		r = cast(int) dist(startX, startY, sxHalf, syHalf );
		if ( rot ) {
			baseRad = 512 - (sin.atan( cast(int)(startX-sxHalf), cast(int) (syHalf-startY) ) >> 7);
			startX = sxHalf + sin.cos( baseRad + rad, r );
			startY = syHalf + sin.sin( baseRad + rad, r );
		}
		
		// 右下に描画
		for ( iy = y / LC_SIZE + 1; iy < texMat.length; ++iy) {
			for (ix = x / LC_SIZE + 1; ix < texMat[iy].length; ++ix) {
				curTex = texMat[iy][ix];
				screen.bltRotate( curTex, cast(int) (startX+dw), cast(int)(startY+dh), 
										rad, rate, 0 );
				if (rot) {
					dw += sin.cos( rad, cast(int) (curTex.getWidth() * rate) );
					dh += sin.sin( rad, cast(int) (curTex.getWidth() * rate) );
				} else {
					dw += curTex.getWidth() * rate;
				}
			}
			if (rot) {
				sh = cast(int) (curTex.getHeight() * rate);
				
				startX -= sin.sin(rad, sh);
				startY += sin.cos(rad, sh);
				
				dw = 0;
				dh = 0;
			} else {
				dw = 0;
				dh += curTex.getHeight() * rate;
			}
		}
		
		startX = sxHalf - tpx;
		startY = syHalf - tpy;
		startY += LC_SIZE * rate;
		dw = 0;
		dh = 0;
		r = cast(int) dist(startX, startY, sxHalf, syHalf );
		if ( rot ) {
			baseRad = 512 - (sin.atan( cast(int)(startX-sxHalf), cast(int) (syHalf-startY) ) >> 7);
			startX = sxHalf + sin.cos( baseRad + rad, r );
			startY = syHalf + sin.sin( baseRad + rad, r );
		}
		// 左下に描画
		for ( iy = y / LC_SIZE + 1; iy < texMat.length; ++iy) {
			for (ix = x / LC_SIZE; ix >= 0; --ix) {
				curTex = texMat[iy][ix];
				screen.bltRotate( curTex, cast(int) (startX+dw), cast(int)(startY+dh), 
										rad, rate, 0 );
				if (rot) {
					dw -= sin.cos( rad, cast(int) (curTex.getWidth() * rate) );
					dh -= sin.sin( rad, cast(int) (curTex.getWidth() * rate) );
				} else {
					dw -= curTex.getWidth() * rate;
				}
			}
			if (rot) {
				sh = cast(int) (curTex.getHeight() * rate);
				
				startX -= sin.sin(rad, sh);
				startY += sin.cos(rad, sh);
				
				dw = 0;
				dh = 0;
			} else {
				dw = 0;
				dh += curTex.getHeight() * rate;
			}
		}
		
		
		
	}	
	
	/// 仮想テクスチャの幅を返却する
	float getWidth() {
		
		if ( texMat is null ) return 0.0f;
		
		float w = 0.0f;
		foreach ( inout Texture t; texMat[0] ) {
			w += t.getWidth();
		}
		
		return w;
	}
	
	/// 仮想テクスチャの高さを返却する
	float getHeight() {
		if ( texMat is null ) return 0.0f;

		float h = 0.0f;
		foreach ( inout Texture[] texs; texMat ) {
			h += cast(int) texs[0].getHeight();
		}
		
		return h;
	}
	
	/// 静的コンストラクタ
	static this() {
		sin = SinTable.get();
	}
	
private:

    /// 2点間の距離を返す
    private static double dist(double x1, double y1, double x2, double y2) {
        return .sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    }
    
	static const uint LC_ATAN_RAD = (65535 / 512);
	static SinTable sin;

	Texture[][] texMat;
}