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.scale(params.x, params.y);
				c.transform.apply(c, this.getMatrix(
					entity.width,
					entity.height,
					params.x,
					params.y,
					0
				));
			},
			rotate: (c:CanvasRenderingContext2D, entity:E, params:any) => {
				//c.rotate(params);
				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, area?:Area) {
		if (! area) {
			area = new Area(
				Math.round(parent.x),
				Math.round(parent.y),
				parent.width,
				parent.height
			);
		}

		if (parent.orderDraw)
			parent.orderDraw();

		c.save();
		if (parent.options)
			this.useDrawOption(parent, c);

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

		c.restore();
	}

	renderEntity(area:Area, entity:E, c:CanvasRenderingContext2D) {
		if (entity.disableTransform) {
			entity.draw(area, c);
		} else {
			c.save();
			c.translate(area.x + entity.x, area.y + entity.y);
			
			if (entity.options)
				this.useDrawOption(entity, c);

			if (entity.filter)
				this.filterDraw(area, entity, c);
			else
				entity.draw(area, c);

			if (entity.entities)
				for (var i=0; i<entity.entities.length; i++)
					this.renderEntity(new Area(0,0,entity.width,entity.height), entity.entities[i], c);

			c.restore();
		}
	}

	renderPure(area:Area, entity:E, c:CanvasRenderingContext2D) {
		entity.draw(area, c);
	}

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

	useDrawOption(entity:E, c:CanvasRenderingContext2D) {
		for (var p in entity.options) {
			if (this.drawOptionFunctions[p]) {
				this.drawOptionFunctions[p].call(this, c, entity, entity.options[p]);
			} else {
				c[p] = entity.options[p];
			}
		}
	}
}