class Renderer {
	radian:number;
	drawOptionFunctions:Object;
	filter: ImageFilter.IFilter;

	constructor() {
		this.radian = Math.PI / 180;

		this.drawOptionFunctions = {
			transform: (c:CanvasRenderingContext2D, entity:E, params:any) => {
				c.transform(
					params.m11,
					params.m12,
					params.m21,
					params.m22,
					params.dx,
					params.dy
				);
			},
			translate: (c:CanvasRenderingContext2D, entity:E, params:any) => {
				c.translate(params.x, params.y);
			},
			scale: (c:CanvasRenderingContext2D, entity:E, params:any) => {
				c.transform.apply(c, this.getMatrix(
					entity.width,
					entity.height,
					params.x,
					params.y,
					0
				));
			},
			rotate: (c:CanvasRenderingContext2D, entity:E, params:any) => {
				c.transform.apply(c, this.getMatrix(
					entity.width,
					entity.height,
					1,
					1,
					params
				));
			}
		}
	}

	getMatrix(width:number, height:number, scaleX:number, scaleY:number, angle:number) {
		var r = angle * this.radian;
		var _cos = Math.cos(r);
		var _sin = Math.sin(r);
		var a = _cos * scaleX;
		var b = _sin * scaleX;
		var c = _sin * scaleY;
		var d = _cos * scaleY;
		var w = width / 2;
		var h = height / 2;
		return [
			a,
			b,
			-c,
			d,
			(-a * w + c * h + w),
			(-b * w - d * h + h)
		]
	}

	renderParent(parent:E, c:CanvasRenderingContext2D) {
		if (parent.orderDraw)
			parent.orderDraw();

		c.save();
		if (parent.options) {
			if (this.useDrawOption(parent, c)) {	//globalAlpha == 0のhidden処理
				c.restore();
				return;
			}
		}

		//これはいらない（描画対象はentity.x, entity.yで移動する。LayerはRenderer側で描画場所を指定する）
		//if (parent.x || parent.y)
		//	c.translate(parent.x, parent.y);

		if (parent.scroll)
			c.translate(parent.scroll.x, parent.scroll.y);

		for (var i=0; i<parent.entities.length; i++)
			this.renderEntity(parent.entities[i], c);

		c.restore();
	}

	renderEntity(entity:E, c:CanvasRenderingContext2D) {
		if (entity.disableTransform) {
			entity.draw(c);
		} else {
			c.save();
			c.translate(entity.x, entity.y);
			
			if (entity.options) {
				if (this.useDrawOption(entity, c)) {	//globalAlpha == 0のhidden処理
					c.restore();
					return;
				}
			}

			if (entity.filter) {
				this.filterDraw(entity, c);
			} else {
				entity.draw(c);
				if (entity.entities) {
					if (entity.scroll)
						c.translate(entity.scroll.x, entity.scroll.y);
					for (var i=0; i<entity.entities.length; i++)
						this.renderEntity(entity.entities[i], c);
				}
			}


			c.restore();
		}
	}

	renderPure(entity:E, c:CanvasRenderingContext2D) {
		entity.draw(c);
		if (entity.entities) {
			for (var i=0; i<entity.entities.length; i++)
				this.renderEntity(entity.entities[i], c);
		}
	}

	//この処理は非常に遅いが、canvas apiはdrawImageにフィルタをかける方法を現状サポートしていないためやむをえない
	filterDraw(entity:E, c:CanvasRenderingContext2D) {
		var buffer = new BufferedRenderer(entity);
		buffer.filter = entity.filter;
		buffer.renderPure(entity, buffer.c);
		buffer.applyFilter(buffer.c, entity);
		buffer.draw(c);
	}

	useDrawOption(entity:E, c:CanvasRenderingContext2D):any {
		for (var p in entity.options) {
			if (this.drawOptionFunctions[p])
				this.drawOptionFunctions[p].call(this, c, entity, entity.options[p]);
			else {
				if (entity.options[p] === 0 && p == "globalAlpha")
					return true;
				c[p] = entity.options[p];
			}
		}
	}
}