enum RenderTransferMode {
	Transfer,
	Flip,
	Direct
}
class GameRenderer extends Renderer {
	buffer: HTMLCanvasElement[];
	fc: CanvasRenderingContext2D;
	bc: CanvasRenderingContext2D;
	scene: Scene;
	game: Game;
	bg:ImageData;
	container:HTMLElement;
	flipNo:number;
	transferMode:RenderTransferMode;
	disableClear:bool;

	constructor(game:Game, container?:HTMLElement, transferMode?:RenderTransferMode) {
		super();
		this.game = game;
		this.container = container ? container : document.getElementById("jgame");
		if (! this.container) {
			var div = document.createElement("div");
			div.id = "jgame";
			var bodies = document.getElementsByTagName("body");
			if (bodies.length == 0)
				throw "can not initialize game engine";

			bodies[0].appendChild(div);
			this.container = div;
		}

		this.changeTransferMode(transferMode ? transferMode : RenderTransferMode.Transfer);

		this.bg = this.fc.getImageData(0, 0, this.game.width, this.game.height);
		for (var i=0; i<this.bg.data.length; i++) {
			this.bg.data[i] = 255;
		}
	}

	changeTransferMode(mode:RenderTransferMode) {
		this.transferMode = mode;
		if (this.transferMode == RenderTransferMode.Flip) {
			this.container.style.position = "relative";
			this.container.style.width = this.game.width + "px";
			this.container.style.height = this.game.height + "px";
		}
		this.refresh();
	}

	changeScene(scene:Scene) {
		this.scene = scene;
	}

	flip() {
		var c = this.fc;
		this.fc = this.bc;
		this.bc = this.fc;
		this.flipNo = this.flipNo ? 0 : 1;

		//ちらつくし遅い
		//this.buffer[this.flipNo].style.zIndex = "1";
		//this.buffer[this.flipNo].style.visibility = "visible";
		//this.buffer[this.flipNo ? 0 : 1].style.zIndex = "0";
		//this.buffer[this.flipNo ? 0 : 1].style.visibility = "hidden";

		//遅い
		this.buffer[this.flipNo].style.zIndex = "1";
		this.buffer[this.flipNo ? 0 : 1].style.zIndex = "0";

		//ちらつく上にかなり遅い
		//this.buffer[this.flipNo].style.display = "block";
		//this.buffer[this.flipNo ? 0 : 1].style.display = "none";

		//ちらつく
		//this.buffer[this.flipNo].style.visibility = "visible";
		//this.buffer[this.flipNo ? 0 : 1].style.visibility = "hidden";
	}

	render() {
		if (this.scene.layerCount == 1) {
			// 単一レイヤーの場合、バックバッファに直接描く
			var layer = this.scene.layers["root"];
			if (! layer.isUpdate()) {
			} else {
				if (!this.disableClear)
					this.bc.putImageData(this.bg, 0, 0);
				this.renderParent(layer, this.bc);
				layer.reflected();
				if (this.transferMode == RenderTransferMode.Flip) {
					this.flip();
				} else if (this.bc != this.fc) {
					this.fc.drawImage(this.buffer[1], 0, 0);
				}
			}
		} else {
			// 無駄な処理だけど、更新検知ちゃんとした方が最終的には軽い、と思う
			var hasUpdate:bool = false;
			for (var i in this.scene.layers) {
				if (this.scene.layers[i].isUpdate()) {
					hasUpdate = true;
					break;
				}
			}

			if (hasUpdate) {
				if (!this.disableClear)
					this.bc.putImageData(this.bg, 0, 0);
				for (var i in this.scene.layers) {
					var layer = this.scene.layers[i];
					if (layer.isUpdate()) {
						layer.context.clearRect(0, 0, this.game.width, this.game.height);
						this.renderParent(layer, layer.context);
					}
					this.bc.drawImage(layer.canvas, 0, 0);
					layer.reflected();
				}

				//Note: getImageDataをするくらいならdrawImageのが速い模様
				//this.fc.putImageData(this.bc.getImageData(0, 0, this.width, this.height), 0, 0);
				//this.fc.drawImage(this.buffer, 0, 0);
				if (this.transferMode == RenderTransferMode.Flip) {
					this.flip();
				} else if (this.bc != this.fc) {
					this.fc.drawImage(this.buffer[1], 0, 0);
				}
			}
		}
	}

	refresh() {
		delete this.buffer;
		this.buffer = new HTMLCanvasElement[];

		if (this.transferMode == RenderTransferMode.Flip) {
			this.container.innerHTML = "";
			for (var i=0; i<2; i++) {
				this.buffer[i] = window.createCanvas(this.game.width, this.game.height);;
				this.buffer[i].style.position="absolute";
				this.buffer[i].style.zIndex = i.toString();
				this.container.appendChild(this.buffer[i]);
			}
			this.fc = this.buffer[1].getContext("2d");
			this.bc = this.buffer[0].getContext("2d");
			this.flipNo = 1;
		} else if (this.transferMode == RenderTransferMode.Transfer) {
			this.container.innerHTML = "";
			for (var i=0; i<2; i++) {
				this.buffer[i] = window.createCanvas(this.game.width, this.game.height);;
			}
			this.container.appendChild(this.buffer[0]);
			this.fc = this.buffer[0].getContext("2d");
			this.bc = this.buffer[1].getContext("2d");
		} else {
			//Directモードはバッファを一つしか作らず、
			this.container.innerHTML = "";
			this.buffer[0] = window.createCanvas(this.game.width, this.game.height);;
			this.container.appendChild(this.buffer[0]);
			this.fc = this.buffer[0].getContext("2d");
			this.bc = this.fc;
		}
	}
}