module jg {
	export var ENTITY_OPTIONS_DEFAULT_VALUES = {
		rotate: 0,
		translate: {x: 0, y: 0},
		transform: {m11: 1, m12: 0, m21: 0, m22: 1, dx: 0, dy: 0},
		scale: {x: 1, y: 1},
		globalAlpha: undefined,
		font: undefined,
		fillStyle: undefined,
		strokeStyle: undefined,
		lineCap: undefined,
		lineJoin: undefined,
		lineWidth: undefined,
		miterLimit: undefined,
		shadowBlur: undefined,
		shadowColor: undefined,
		shadowOffsetX: undefined,
		shadowOffsetY: undefined,
		strokeStyle: undefined,
		textAlign: undefined,
		textBaseline: undefined,
		globalCompositeOperation: undefined
	};

	export class E {
		x: number;
		y: number;
		width: number;
		height: number;
		_tl: Timeline;
		scene:Scene;
		parent:E;
		active_queue:Function[];
		started:bool;
		isUpdated:bool;
		disableTransform:bool;
		entities:E[];
		pointCapture:bool;
		pointDown: Trigger;
		pointUp: Trigger;
		pointMove: Trigger;
		//rotate: number
		//translate: {x: number, y: number}
		//transform: {m11: number, m12: number, m21: number, m22: number, dx: number, dy: number}
		//scale: {x: number, y: number}
		//fillStyle: string(color)
		//font: string
		//globalAlpha: number
		//lineCap: string(butt, round, square)
		//lineJoin: string(bevel, round, miter)
		//lineWidth: number
		//miterLimit: number
		//shadowBlur: number
		//shadowColor: string(color)
		//shadowOffsetX: number
		//shadowOffsetY: number
		//strokeStyle: string(color)
		//textAlign: string(start, end, left, right, center)
		//textBaseline: string(top, hanging, middle, alphabetic, ideographic, bottom)
		//globalCompositeOperation: string(source-atop, source-in, source-out, source-over (default), destination-atop, destination-in, destination-out, destination-over, lighter, copy, xor, vendorName-operationName)
		options:Object;
		orderDraw:Function;
		filter:ImageFilter.FilterChain;
		scroll:CommonOffset;
		opacity:number;

		constructor() {
			this.opacity = 1;
			this.x = 0;
			this.y = 0;
		}

		enablePointingEvent() {
			this.pointCapture = true;
			if (! this.pointDown)
				this.pointDown = new Trigger();
			if (! this.pointUp)
				this.pointUp = new Trigger();
			if (! this.pointMove)
				this.pointMove = new Trigger();
		}

		disablePointingEvent() {
			delete this.pointCapture;
		}

		removeDrawOption(name:string) {
			if (! this.options)
				return;

			if (this.options[name] !== undefined)
				delete this.options[name];
			var cnt = 0;
			for (var i in this.options) {
				cnt++;
				break;
			}
			if (! cnt)
				delete this.options;
			this.updated();
		}

		setDrawOption(name:string, value:any) {
			if (! this.options)
				this.options = {}
			this.options[name] = value;
			this.updated();
		}

		getDrawOption(name:string):any {
			if (! this.options || this.options[name] == undefined)
				return ENTITY_OPTIONS_DEFAULT_VALUES[name];
			return this.options[name];
		}

		moveTo(x:number, y: number) {
			this.x = x;
			this.y = y;
			this.updated();
		}

		moveBy(x:number, y:number) {
			this.x += x;
			this.y += y;
			this.updated();
		}

		scrollTo(x:number, y: number) {
			this.scroll = {
				x: x,
				y: y
			}
			this.updated();
		}

		scrollBy(x:number, y:number) {
			if (! this.scroll)
				this.scroll = {x: 0, y: 0}
			this.scroll.x += x;
			this.scroll.y += y;
			this.updated();
		}

		activate() {
			if (this.active_queue) {
				var f:Function;
				while (f = this.active_queue.shift())
					f.call(this);

				delete this.active_queue;
			}
			if (this.entities) {
				for (var i=0; i<this.entities.length; i++) {
					if (! this.entities[i].scene) {
						this.entities[i].scene = this.scene;
						this.entities[i].activate();
					}
				}
			}
		}

		addActiveQueue(f:Function) {
			if (this.scene) {
				f.call(this);
				return;
			}
			if (! this.active_queue)
				this.active_queue = [];
			this.active_queue.push(f);
		}

		appendTo(scene:Scene, layerName?:string) {
			scene.append(this, layerName);
		}

		remove() {
			if (this.parent)
				this.parent.removeChild(this);
			else
				throw "Can not remove layer. (use scene.deleteLayer)";
		}

		insert(entity:E, index: any) {
			if (! this.entities)
				throw "Can not call append of non-container entity";
			entity.scene = this.scene;
			entity.parent = this;
			if (typeof index != "number") {
				for (var i=0; i<this.entities.length; i++) {
					if (this.entities[i] == index) {
						index = i;
						break;
					}
				}

			}
			this.entities.splice(index, 0, entity);
			entity.activate();
			this.updated();
		}

		append(entity:E) {
			if (! this.entities)
				throw "Can not call append of non-container entity";
			entity.scene = this.scene;
			entity.parent = this;
			this.entities.push(entity);
			if (this.scene)
				entity.activate();
			this.updated();
		}

		removeChild(entity:E) {
			if (! this.entities)
				throw "Can not call removeChild of non-container entity";

			for (var i=0; i<this.entities.length; i++) {
				if (this.entities[i] == entity) {
					if (entity.entities) {
						var childEntity;
						while (childEntity = entity.entities.pop())
							entity.removeChild(childEntity);
					}
					this.entities.splice(i, 1);
					entity.destroy();
					this.updated();
					return true;
				}
			}
			return false;
		}

		start() {
			if (this.started)
				return;
			this.started = true;
			if (this.scene)
				this.scene.game.update.handle(this, this.update);
			else
				this.addActiveQueue(function() {this.scene.game.update.handle(this, this.update);} );
		}

		stop() {
			if (! this.started)
				return;
			this.started = false;
			if (this.scene)
				this.scene.game.update.remove(this, this.update);
			else
				this.addActiveQueue(function() {this.scene.game.update.remove(this, this.update);});
		}

		startTimer(wait:number, method?:Function) {
			if (this.scene)
				this.scene.game.addTimer(wait, this, method ? method : this.interval);
			else
				this.addActiveQueue(function() {this.scene.game.addTimer(wait, this, method ? method : this.interval);});
		}

		stopTimer(wait:number, method?:Function) {
			if (this.scene)
				this.scene.game.removeTimer(wait, this, method ? method : this.interval)
			else
				this.addActiveQueue(function() {this.scene.game.removeTimer(wait, this, method ? method : this.interval);});
		}

		updated() {
			var p = this;
			while (p.parent)
				p = p.parent;
			p.isUpdated = true;
		}

		isUpdate():bool {
			return this.isUpdated;
		}

		reflected() {
			this.isUpdated = false;
		}

		tl():Timeline {
			if (! this._tl)
				this._tl = new Timeline(this);
			return this._tl;
		}

		destroy() {
			if  (this._tl) {
				this._tl.clear();
				delete this._tl;
			}
			this.stop();
			if (this.scene) {
				this.scene.game.removeTimerAll(this);
				this.scene = null;
			}
			delete this.parent;
			if (this.entities) {
				var childEntity;
				while (childEntity = this.entities.pop())
					childEntity.destroy();
			}
			if (this.pointDown) {
				this.pointDown.destroy();
				delete this.pointDown;
			}
			if (this.pointUp) {
				this.pointUp.destroy();
				delete this.pointUp;
			}
			if (this.pointMove) {
				this.pointMove.destroy();
				delete this.pointMove;
			}
		}

		offset():CommonOffset {
			var parent_offset = this.parent ? this.parent.offset() : {x:this.scroll ? this.scroll.x : 0, y: this.scroll ? this.scroll.y : 0};
			return {
				x: this.x + parent_offset.x,
				y: this.y + parent_offset.y
			}
		}

		rect():Rectangle {
			var offset = this.offset();
			return new Rectangle(
				offset.x,
				offset.y,
				offset.x + this.width,
				offset.y + this.height
			);
		}

		hitTest(point: CommonOffset) {
			return this.rect().hitTest(point);
		}

		getDistance(point:CommonOffset):CommonOffset {
			var area:CommonArea = <CommonArea>point;
			if (area.width && area.height) {
				return {
					x: Math.abs((area.x+area.width/2) - (this.x+this.width/2)),
					y: Math.abs((area.y+area.height/2) - (this.y+this.height/2))
				};
			} else {
				return {
					x: Math.abs(point.x - (this.x+this.width/2)),
					y: Math.abs(point.y - (this.y+this.height/2))
				};
			}
		}

		getEntityByPoint(point: CommonOffset, force?:bool):E {
			if (this.entities) {
				for (var i=this.entities.length-1; i>=0; i--) {
					if (force || this.entities[i].pointCapture) {
						var p = this.entities[i].getEntityByPoint(point);
						if (p)
							return p;
					}
				}
			}
			if ((force || this.pointCapture) && this.hitTest(point))
				return this;

			return null;
		}

		createSprite() {
			var buffer = new BufferedRenderer({width:this.width, height:this.height});
			var x = this.x;
			var y = this.y;
			this.x = 0;
			this.y = 0;
			buffer.renderUnit(this);
			this.x = x;
			this.y = y;
			return buffer.createSprite();
		}

		update(t:number) {}

		interval() {}

		draw(context:CanvasRenderingContext2D) {}

		show() {
			this.opacity = 1;
		}
		hide() {
			this.opacity = 0;
		}
	}
}

(function() {
	var canvas:HTMLCanvasElement = <HTMLCanvasElement>document.createElement("canvas");
	var context = canvas.getContext("2d");
	for (var p in jg.ENTITY_OPTIONS_DEFAULT_VALUES)
		if (jg.ENTITY_OPTIONS_DEFAULT_VALUES[p] == undefined)
			jg.ENTITY_OPTIONS_DEFAULT_VALUES[p] = context[p];
})();
