enum EffectType {
	None,
	Fade,
	Mosaic,
	Blur,
	SlideUp,
	SlideDown,
	SlideLeft,
	SlideRight,
	WipeUp,
	WipeDown,
	WipeLeft,
	WipeRight,
	WipeFadeUp,
	WipeFadeDown,
	WipeFadeLeft,
	WipeFadeRight,
	BoxOut,
	BoxOut45,
	BoxIn,
	BoxIn45,
	ArcOut,
	ArcIn,
	BoxOutBlack,
	BoxOut45Black,
	BoxInBlack,
	BoxIn45Black,
	ArcOutBlack,
	ArcInBlack,
	BoxOutWhite,
	BoxOut45White,
	BoxInWhite,
	BoxIn45White,
	ArcOutWhite,
	ArcInWhite
}
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 {
			this.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);
	}
}
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.setDrawOption("globalAlpha", 0);
		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.setDrawOption("globalAlpha", 0);
		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.setDrawOption("globalAlpha", 0);
			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.setDrawOption("globalAlpha", 0);
			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.setDrawOption("globalAlpha", 0);
			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.setDrawOption("globalAlpha", 0);
			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();
		});
	}

	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;
	}
}