///<reference path="all.ts"/>
module jg {
	/**
	 * バッファ上に書き込む特殊なRenderer
	 */
	export class BufferedRenderer extends Renderer {
		/** バッファ */
		buffer: HTMLCanvasElement;
		/** 描画用context */
		c: CanvasRenderingContext2D;
		/** このバッファのサイズ */
		size: CommonSize;

		/**
		 * コンストラクタ
		 * @param size バッファサイズ
		 */
		constructor(size:CommonSize) {
			super();
			this.size = size;
			this.refresh();
		}

		/**
		 * バッファを透明でクリアする
		 */
		clear() {
			this.c.clearRect(0, 0, this.size.width, this.size.height);
		}

		/**
		 * バッファの描画内容を基に画像を生成する
		 * @param area コピー元バッファ領域。省略時は全画面
		 * @param distArea コピー先バッファ領域。省略時はx:0, y:0, width:area.width, height:area.height
		 * @param canavsSize 出力イメージサイズ。省略時はx: 0, y:0, width: area.width, height: area.height
		 */
		createImage(area?:CommonArea, distArea?:CommonArea, canvasSize?:CommonSize):HTMLCanvasElement {
			if (! area)
				area = {x: 0, y: 0, width: this.size.width, height: this.size.height};
			if (! distArea)
				distArea = {x: 0, y: 0, width: area.width, height: area.height}
			if (! canvasSize)
				canvasSize = area;

			var canvas:HTMLCanvasElement = window.createCanvas(canvasSize.width, canvasSize.height);

			var context = canvas.getContext("2d");
			context.drawImage(
				this.buffer,
				area.x,
				area.y,
				area.width,
				area.height,
				distArea.x,
				distArea.y,
				distArea.width,
				distArea.height
			);
			if (this.filter)
				this.applyFilter(context, distArea);
			return canvas;
		}

		/**
		 * バッファの内容を基にSpriteを生成する
		 * @param area コピー元バッファ領域。省略時は全画面
		 * @param distArea コピー先バッファ領域。省略時はx:0, y:0, width:area.width, height:area.height
		 * @param canavsSize Spriteが保持する画像のサイズ。省略時はx: 0, y:0, width: area.width, height: area.height
		 */
		createSprite(area?:CommonArea, distArea?:CommonArea, canvasSize?:CommonSize) {
			if (! area)
				area = {x: 0, y: 0, width: this.size.width, height: this.size.height};
			if (! distArea)
				distArea = {x: 0, y: 0, width: area.width, height: area.height}
			return new Sprite(
				this.createImage(area, distArea, canvasSize),
				area.width,
				area.height
			);
		}

		/**
		 * フィルタを適用する
		 * @param c フィルタ適用対象context
		 * @param size フィルタを適用するサイズ
		 */
		applyFilter(c:CanvasRenderingContext2D, size:CommonSize) {
			var imageData = c.getImageData(0, 0, size.width, size.height);
			this.filter.filter(imageData);
			c.putImageData(imageData, 0, 0);
		}

		/**
		 * オブジェクトをこのバッファに描画する
		 * @param entity 描画対象オブジェクト
		 */
		renderUnit(entity:E) {
			var area:CommonArea = {x: 0, y: 0, width: entity.width, height: entity.height}
			this.renderEntity(entity, this.c);
		}

		/**
		 * レイヤーをこのバッファに描画する
		 * @param layer 描画対象レイヤー
		 */
		renderLayer(layer:Layer) {
			this.renderParent(layer, this.c);
		}

		/**
		 * シーンをこのバッファに描画する
		 * @param scene 描画対象シーン
		 */
		renderScene(scene:Scene) {
			this.clear();
			for (var i in scene.layers) {
				this.c.save();
				var layer = scene.layers[i];
				if (layer.x || layer.y)
					this.c.translate(layer.x, layer.y);
				this.renderLayer(layer);
				this.c.restore();
			}
		}

		/**
		 * バッファの内容を再生成する。この際、描画済み情報は失われる。
		 * この処理はPCがスリープ状態から復帰した時などに呼び出される可能性があるため、BufferedRendererに描画済みの情報を長期間参照し続けることは推奨されない。。
		 */
		refresh() {
			delete this.buffer;
			this.buffer = window.createCanvas(this.size.width, this.size.height);
			this.c = this.buffer.getContext("2d");
		}

		/**
		 * このバッファを別の対象に描画する
		 * @param context 描画対象context
		 */
		draw(context:CanvasRenderingContext2D) {
			context.drawImage(
				this.buffer,
				0,
				0,
				this.size.width,
				this.size.height,
				0,
				0,
				this.size.width,
				this.size.height
			);
		}
	}
}
