module jg {
	export class Effect {
		static time:number = 1000;
		static color:string;
		static sceneEffect(game:Game, scene1:Scene, scene2:Scene, type:any, callback:Function, endOldScene?:bool) {
			var effect = new EffectScene(game, scene1, scene2);
			if (endOldScene) {
				game.endScene();
				if (game._exit)	//処理続行不可能
					return;
			}
			game.changeScene(effect);
			effect.effected.handle(game, callback);
			if (typeof type == "number") {
				switch (type) {
				case EffectType.Fade:
					effect.fade(Effect.color);
				break;
				case EffectType.Mosaic:
					effect.mosaic();
				break;
				case EffectType.Blur:
					effect.blur();
				break;
				case EffectType.SlideUp:
					effect.slide(Angle.Up);
				break;
				case EffectType.SlideDown:
					effect.slide(Angle.Down);
				break;
				case EffectType.SlideLeft:
					effect.slide(Angle.Left);
				break;
				case EffectType.SlideRight:
					effect.slide(Angle.Right);
				break;
				case EffectType.WipeUp:
					effect.wipe(Angle.Up);
				break;
				case EffectType.WipeDown:
					effect.wipe(Angle.Down);
				break;
				case EffectType.WipeLeft:
					effect.wipe(Angle.Left);
				break;
				case EffectType.WipeRight:
					effect.wipe(Angle.Right);
				break;
				case EffectType.WipeFadeUp:
					effect.wipeFade(Angle.Up);
				break;
				case EffectType.WipeFadeDown:
					effect.wipeFade(Angle.Down);
				break;
				case EffectType.WipeFadeLeft:
					effect.wipeFade(Angle.Left);
				break;
				case EffectType.WipeFadeRight:
					effect.wipeFade(Angle.Right);
				break;
				case EffectType.BoxOut:
					effect.boxOut(0, Effect.color);
				break;
				case EffectType.BoxOut45:
					effect.boxOut(45, Effect.color);
				break;
				case EffectType.BoxIn:
					effect.boxIn(0, Effect.color);
				break;
				case EffectType.BoxIn45:
					effect.boxIn(45, Effect.color);
				break;
				case EffectType.ArcIn:
					effect.arcIn(Effect.color);
				break;
				case EffectType.ArcOut:
					effect.arcOut(Effect.color);
				break;
				}
			} else {
				type.callEffect(effect);
			}
		}

		method:string;
		arguments:any[];

		constructor(method:string) {
			this.method = method;
			this.arguments = [];
			for (var i=1; i<arguments.length; i++)
				this.arguments.push(arguments[i]);
		}

		callEffect(scene:EffectScene) {
			scene[this.method].apply(scene, this.arguments);
		}
	}

	export class EffectScene extends Scene {
		sp1:Sprite;
		sp2:Sprite;
		effected:Trigger;

		constructor(game:Game, scene1:Scene, scene2:Scene) {
			super(game);
			var sp1 = this.captureScene(scene1);
			var sp2 = this.captureScene(scene2);
			sp2.hide();
			sp1.x = sp1.y = sp2.x = sp2.y = 0;
			this.append(sp1);
			this.append(sp2);
			this.sp1 = sp1;
			this.sp2 = sp2;
			this.effected = new Trigger();
		}

		captureScene(scene:Scene) {
			var buffer = new BufferedRenderer(this.game);
			buffer.renderScene(scene);
			return buffer.createSprite();
		}

		fade(color?:any) {
			if (color) {
				this._fadeColor(color);
				return;
			}
			this.sp1.tl().fadeOut(Effect.time).removeFromScene();
			this.sp2.tl().fadeIn(Effect.time).then(() => {
				this.effected.fire();
			});
		}

		_fadeColor(color:any) {
			var shape = new Shape(
				this.sp1.width,
				this.sp1.height,
				ShapeStyle.Fill,
				color
			);
			shape.hide();
			this.root.append(shape);
			var t = Effect.time / 2;
			this.sp1.tl().fadeOut(t);
			shape.tl().fadeIn(t).fadeOut(t).removeFromScene();
			this.sp2.tl().delay(t).fadeIn(t).then(() => {
				this.effected.fire();
			});
		}

		mosaic() {
			var t = Effect.time / 2;
			this.sp1.tl().filter(ImageFilter.MosaicFilter, {size:{start:1, end:64}}, t).removeFromScene();
			this.sp2.tl().delay(t).show().filter(ImageFilter.MosaicFilter, {size:{start:64, end:1}}, t).then(() => {
				this.effected.fire();
			});
		}

		blur() {
			var t = Effect.time / 2;
			this.sp1.tl().filter(ImageFilter.BlurFilter, {amount:{start:1, end:20}}, t, Easing.CUBIC_EASEIN).removeFromScene();
			this.sp2.tl().delay(t).show().filter(ImageFilter.BlurFilter, {amount:{start:20, end:1}}, t, Easing.CUBIC_EASEOUT).then(() => {
				this.effected.fire();
			});
		}

		slide(angle:Angle) {
			var t = Effect.time;
			switch(angle) {
				case Angle.Up:
					this.sp1.tl().moveTo(0, -this.game.height, t).removeFromScene();
					this.sp2.moveTo(0, this.game.height);
					this.sp2.tl().show().moveTo(0, 0, t);
				break;
				case Angle.Down:
					this.sp1.tl().moveTo(0, this.game.height, t).removeFromScene();
					this.sp2.moveTo(0, -this.game.height);
					this.sp2.tl().show().moveTo(0, 0, t);
				break;
				case Angle.Left:
					this.sp1.tl().moveTo(-this.game.width, 0, t).removeFromScene();
					this.sp2.moveTo(this.game.width, 0);
					this.sp2.tl().show().moveTo(0, 0, t);
				break;
				case Angle.Right:
					this.sp1.tl().moveTo(this.game.width, 0, t).removeFromScene();
					this.sp2.moveTo(-this.game.width, 0);
					this.sp2.tl().show().moveTo(0, 0, t);
				break;
			}
			this.sp2.tl().then(() => {
				this.effected.fire();
			});
		}

		wipe(angle:Angle) {
			var t = Effect.time;
			this.sp2.tl().show();
			var sp = new Shape(this.game.width, 1, ShapeStyle.Fill);
			sp.moveTo(0, 0);
			sp.setClip(true);
			this.root.insert(sp, 1);
			switch(angle) {
				case Angle.Up:
					sp.width = this.game.width;
					sp.height = 1;
					sp.y = this.game.height;
					sp.tl().resizeTo(this.game.width, this.game.height, t).and().moveTo(0, 0, t);
				break;
				case Angle.Down:
					sp.width = this.game.width;
					sp.height = 1;
					sp.tl().resizeTo(this.game.width, this.game.height, t);
				break;
				case Angle.Left:
					sp.width = 1;
					sp.height = this.game.height;
					sp.x = this.game.width;
					sp.tl().resizeTo(this.game.width, this.game.height, t).and().moveTo(0, 0, t);
				break;
				case Angle.Right:
					sp.width = 1;
					sp.height = this.game.height;
					sp.tl().resizeTo(this.game.width, this.game.height, t);
				break;
			}
			sp.tl().then(() => {
				this.effected.fire();
			});
		}

		wipeFade(angle:Angle) {
			this.wipe(angle);
			this.sp1.tl().fadeOut(Effect.time);
		}

		boxOut(rotate?:number, color?:any) {
			var t = Effect.time;
			this.sp2.tl().fadeIn(t);
			var sp = new Shape(1, 1, ShapeStyle.Fill);
			sp.moveTo(this.game.width / 2, this.game.height / 2);
			sp.setClip(true);
			if (rotate)
				sp.setDrawOption("rotate", rotate);

			this.root.insert(sp, 1);

			if (color) {
				var bg = new Shape(this.game.width, this.game.height, ShapeStyle.Fill, color);
				this.root.insert(bg, 1);
				bg.hide();
				bg.tl().fadeIn(t*0.6);
			}
			//scaleだとclipがきかない模様
			if (rotate)
				sp.tl().resizeTo(this.game.width*2, this.game.height*2, t).and().moveTo(-this.game.width/2, -this.game.height/2, t);
			else
				sp.tl().resizeTo(this.game.width, this.game.height, t).and().moveTo(0, 0, t);
			sp.tl().then(() => {
				this.effected.fire();
			});
		}

		//本当は四隅に四つ四角を配置しないといけない。この実装は手抜き
		boxIn(rotate?:number, color?:any) {
			this.swapScene();
			var t = Effect.time;
			this.sp2.tl().fadeIn(t);
			var sp = new Shape(1, 1, ShapeStyle.Fill);
			if (rotate) {
				sp.moveTo(-this.game.width/2, -this.game.height/2);
				sp.width = this.game.width * 2;
				sp.height = this.game.height * 2;
			} else {
				sp.moveTo(0, 0);
				sp.width = this.game.width;
				sp.height = this.game.height;
			}
			sp.setClip(true);
			if (rotate)
				sp.setDrawOption("rotate", rotate);
			this.root.insert(sp, 1);

			if (color) {
				var bg = new Shape(sp.width, sp.height, ShapeStyle.Fill, color);
				bg.moveTo(sp.x, sp.y);
				this.root.append(bg);
				bg.hide();
				bg.tl().fadeIn(t*0.8).and().resizeTo(1,1,t).and().moveTo(this.game.width/2, this.game.height/2, t);
				if (rotate)
					bg.setDrawOption("rotate", rotate);
			} else {
				this.sp1.tl().fadeOut(t);
			}

			//scaleだとclipがきかない模様
			sp.tl().resizeTo(1, 1, t).and().moveTo(this.game.width/2, this.game.height/2, t);
			sp.tl().then(() => {
				this.effected.fire();
			});
		}

		arcOut(color?:any) {
			var t = Effect.time;
			this.sp2.tl().fadeIn(t);
			var sp = new Shape(1, 1, ShapeStyle.Fill,"black",ShapeType.Arc);
			sp.moveTo(this.game.width / 2, this.game.height / 2);
			sp.setClip(true);
			this.root.insert(sp, 1);

			if (color) {
				var bg = new Shape(this.game.width, this.game.height, ShapeStyle.Fill, color);
				this.root.insert(bg, 1);
				bg.hide();
				bg.tl().fadeIn(t*0.6);
			}

			sp.tl().resizeTo(this.game.width*2, this.game.height*2, t).and().moveTo(-this.game.width/2, -this.game.height/2, t);
			sp.tl().then(() => {
				this.effected.fire();
			});
		}

		arcIn(color?:any) {
			this.swapScene();
			var t = Effect.time;
			this.sp2.tl().fadeIn(t);
			var sp = new Shape(1, 1, ShapeStyle.Fill, "black", ShapeType.Arc);
			sp.moveTo(-this.game.width/2, -this.game.height/2);
			sp.width = this.game.width*2;
			sp.height = this.game.height*2;
			sp.setClip(true);
			this.root.insert(sp, 1);

			if (color) {
				var bg = new Shape(sp.width, sp.height, ShapeStyle.Fill, color);
				bg.moveTo(sp.x, sp.y);
				this.root.append(bg);
				bg.hide();
				bg.tl().fadeIn(t*0.9).and().resizeTo(1,1,t).and().moveTo(this.game.width/2, this.game.height/2, t);
			} else {
				this.sp1.tl().fadeOut(t);
			}

			sp.tl().resizeTo(1, 1, t).and().moveTo(this.game.width/2, this.game.height/2, t);
			sp.tl().then(() => {
				this.effected.fire();
			});
		}

		universal(image:any, repeat?:bool) {
			var t = Effect.time;
			this.swapScene();
			this.sp1.tl().filter(
				ImageFilter.UniversalTransitionFilter,
				{image: image, repeat: repeat, amount:{start:-255, end:255}},
				t
			).removeFromScene();
			this.sp2.tl().show().delay(t)./*filter(
				ImageFilter.UniversalTransitionFilter,
				{image: image, repeat: repeat, amount:{start:255, end:-255}},
				t
			).*/then(() => {
				this.effected.fire();
			});
		}

		universalTwin(image:any, repeat?:bool) {
			var t = Effect.time;
			this.sp1.tl().filter(
				ImageFilter.UniversalTransitionFilter,
				{image: image, repeat: repeat, amount:{start:-255, end:255}},
				t
			).removeFromScene();
			this.sp2.tl().show().filter(
				ImageFilter.ReverseUniversalTransitionFilter,
				{image: image, repeat: repeat, amount:{start:255, end:-255}},
				t
			).then(() => {
				this.effected.fire();
			});
		}

		universalDelay(image:any, repeat?:bool, color?:any) {
			var t = Effect.time / 2;
			var shape = new Shape(
				this.sp1.width,
				this.sp1.height,
				ShapeStyle.Fill,
				color
			);

			shape.hide();
			this.root.insert(shape, 0);
			this.sp1.tl().filter(
				ImageFilter.UniversalTransitionFilter,
				{image: image, repeat: repeat, amount:{start:-255, end:255}},
				t
			).removeFromScene();
			shape.tl().fadeIn(t).delay(t).removeFromScene();
			this.sp2.tl().delay(t).show().filter(
				ImageFilter.UniversalTransitionFilter,
				{image: image, repeat: repeat, amount:{start:255, end:-255}},
				t
			).then(() => {
				this.effected.fire();
			});
		}

		getFilter(target:any) {
			if (! target.filter)
				target.filter = new ImageFilter.FilterChain();
			return target.filter;
		}

		swapScene() {
			var tmp = this.root.entities[0];
			this.root.entities[0] = this.root.entities[1];
			this.root.entities[1] = tmp;
		}
	}
}