module jg {
	export 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.opacity != 1)
				c.globalAlpha = parent.opacity;
			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.opacity)
				return;

			if (entity.disableTransform) {
				entity.draw(c);
			} else {
				c.save();
				c.translate(entity.x, entity.y);
				if (entity.opacity != 1)
					c.globalAlpha *= entity.opacity;
				
				if (entity.options) {
					if (this.useDrawOption(entity, c)) {
						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 {
					c[p] = entity.options[p];
				}
			}
		}
	}
}