module yamalib.apartregion;

private import y4d;
private import SDL;
private import yamalib.log.log;

/** フラグを持つRect */
struct RectF {
	float	left   = 0.0;	//!< 左
	float	top	   = 0.0;	//!< 上
	float	right  = 0.0;	//!< 右
	float	bottom = 0.0;	//!< 下
	bool	enable = false;	//<! 有効かどうか（これを使うかは任意）

	///	各データを設定する
	void setRect(float left_,float top_,float right_,float bottom_)	{
		left = left_;
		top  = top_;
		right = right_;
		bottom = bottom_;
	}
	
	/// 単純Rectとの互換
	void setRect(inout Rect rc) {
		left = rc.left;
		top = rc.top;
		right = rc.right;
		bottom = rc.bottom;
	}

	/// 幅を取得する
	/**
		left - right をしているだけ。符号がマイナスになる場合もありうる。
	*/
	float getWidth() { return right - left; }

	///	高さを取得する
	/**
		bottom - top をしているだけ。符号がマイナスになる場合もありうる。
	*/
	float getHeight() { return bottom - top; }
	
	/// 単純Rectへのビューを返す
	Rect getRect() {
		Rect rc;
		rc.setRect(left, top, right, bottom);
		return rc;
	}
}


/**
	リージョンを矩形分解するクラス
	描画エフェクトに使うと便利である
	
	通常の描画
	描画矩形のマスク
	で分割描画可能
*/
class ApartRegion {
	/** 
		クラスの文字列表現を返す
		このメソッドは常に有用な情報の文字列表現を返すべきである
	 */
	override public char[] toString() {
		return null;
	}
	
	
	/// アルファ付きテクスチャから分解矩形を生成します
	/// 結果は二次元配列として返却します(二次元であれば分割された縦横サイズがわかる)
	public static RectF[][] apartFromTexture(inout Surface surface, int divWidth, int divHeight) {
		if (null is surface) return null;
		
		Rect rc;
		
		int width = cast(int) surface.getWidth();
		int height = cast(int) surface.getHeight();
		// 分解するサイズ
		int sx;
		int sy;

		sx = width / divWidth;
		sy = height / divHeight;
/*		
		if ( (sx % LC_UNIT_WIDTH) != 0 ) {
			sx += 1;
		}
		if ( (sy % LC_UNIT_HEIGHT) != 0 ) {
			sy += 1;
		}
*/		
		debug {
			Log.print("Surface width:%s\n", width);
			Log.print("surface height:%s\n", height);
			Log.print("div size x:%s\n", sx);
			Log.print("div size y:%s\n", sy);
		}
		
		// サンプルとする矩形二次元配列を作る
		RectF[][] rcSample = new RectF[][sy];
		foreach (inout RectF[] rcs; rcSample[]) {
			rcs = new RectF[sx];
		}
		
		debug {
			Log.print("rcSample.length:%s\n", rcSample.length);
			Log.print("rcSample[].length:%s\n", rcSample[].length);
		}
		
		// サンプリング枠となる矩形データを作る
		for (int i = 0; i < sy; ++i) {
			for (int j = 0; j < sx; ++j) {
				rcSample[i][j].left = j * divWidth;
				rcSample[i][j].right = rcSample[i][j].left + divWidth;
				rcSample[i][j].top = i * divHeight;
				rcSample[i][j].bottom = rcSample[i][j].top + divHeight;
			}
		}
		
		
		
		// 画像と比較し有効なデータを検索、有効な矩形だけの配列を作る
		for (int i = 0; i < sy; ++i) {
			for (int j = 0; j < sx; ++j) {
				// 矩形が有効画像に内に含まれている
				if ( containRect(surface, rcSample[i][j]) ) {
					// フラグを持つRectなのだー
					rcSample[i][j].enable = true;
				}
			}
		}
		
		return rcSample;
	}
	
	/// 矩形の四隅のピクセルがアルファ値０でなければtrue
	private static bool containRect(inout Surface surface, inout RectF rc) {
		int width = cast(int) surface.getWidth();
		int height = cast(int) surface.getHeight();
		// ピクセルデータはRGBAでならんでるからこれでＯＫでそ？
		SDL_Color* pixels = cast(SDL_Color*) surface.getPixels();
		int x,y;
		
		// 左上
		x = cast(int) rc.left;
		y = cast(int) rc.top;
		if ( pixels[ (y * width) + x  ].unused == 0 ) {
			return false;
		}
		
		// 右上
		x = cast(int) rc.right;
		y = cast(int) rc.top;
		if ( pixels[ (y * width) + x  ].unused == 0 ) {
			return false;
		}
		
		// 左下
		x = cast(int) rc.left;
		y = cast(int) rc.bottom;
		if ( pixels[ (y * width) + x  ].unused == 0 ) {
			return false;
		}
		
		// 右下
		x = cast(int) rc.right;
		y = cast(int) rc.bottom;
		if ( pixels[ (y * width) + x  ].unused == 0 ) {
			return false;
		}
		
		return true;
	}
	
	/// 二次元配列を結合する
	/// 結合した結果の配列は１異次元配列となる
	public static RectF[] uniteRect(RectF[][] rects, int uniteX, int uniteY) {
		int sx = rects[].length;
		int sy = rects.length;
		RectF[] rectResult;
		bool[][] flags;
		
		// こないしないとサイズとれへんのか？
		sx = sy = 0;
		foreach(inout RectF[] rectLine; rects) {
			sy++;
			foreach(inout RectF rc; rectLine) {
				sx++;
			}
		}
		sx /= sy;
		debug {
			Log.print("sx : %s\n", sx);
			Log.print("sy : %s\n", sy);
		}
		
		assert( (sy - uniteY) > 0 );
		assert( (sx - uniteX) > 0 );
		
		flags.length = sy;
		foreach (inout bool[] flgs; flags) {
			flgs.length = sx;
			flgs[0..length] = false;
		}
		
		for (int i = 0; i < (sy - uniteY) ; ++i) {
			for (int j = 0; j < (sx - uniteX); ++j) {
				// 結合できるんやろか？
				if ( isUnitable(rects, flags, j, i, uniteX, uniteY) ) {
					// できるそうや...
					RectF rc;
					
					// 左上位置
					rc.left = rects[i][j].left;
					rc.top = rects[i][j].top;
					
					// 右下位置
					rc.right = rects[i + uniteY - 1][j + uniteX - 1].right;
					rc.bottom = rects[i + uniteY - 1][j + uniteX - 1].bottom;
					
					// 有効可否フラグ
					rc.enable = rects[i][j].enable;
					
					rectResult ~= rc;
					
					// 結合されたもののフラグをたてておく
					flagOn(flags, j, i, uniteX, uniteY);
				}
			}
		}
		
		// 結合されなかったものをつけておく
		for (int i = 0; i < sy; ++i) {
			for (int j = 0; j < sx; ++j) {
				if ( !flags[i][j] ) {
					rectResult ~= rects[i][j];
				}
			}
		}
		
		return rectResult;
	}
	
	
	/// 配列のposx,posyの位置からunitex,uniteyサイズの矩形は結合可能であるか？
	private static bool isUnitable(RectF[][] rects, bool[][] flags, int posx, int posy, int uniteX, int uniteY) {
		int result = 0;
		
		for (int i = 0; i < uniteY; ++i) {
			for (int j = 0; j < uniteX; ++j) {
				
				// この区画がすでに使用済みであれば結合失敗
				if ( flags[posy + i][posx + j] ) {
					return false;
				}
				
				if (rects[posy + i][posx + j].enable) {
					result += 1;
				}
			}
		}
		
		// 結果が0(走査結果すべて固定), またはすべて有効ならば true
		return cast(bool) ((result == 0) || ( result == (uniteX * uniteY) ));
	}
	
	/// 結合した区画のフラグをたてる
	private static void flagOn(inout bool[][] flags, int posx, int posy, int uniteX, int uniteY) {
		for (int i = 0; i < uniteY; ++i) {
			for (int j = 0; j < uniteX; ++j) {
				flags[posy + i][posx + j] = true;
			}
		}
	}

}