/*
 * Copyright [yyyy] [name of copyright owner]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


// Anonymous function start
//
(function( window, undefined )
{

// reference
var Config			= window.h5glib.Config;
var Debug			= window.h5glib.Debug;
var Command			= window.h5glib.Command;
var Task			= window.h5glib.Task;
var SoundTask		= window.h5glib.SoundTask;
var ReadyTask		= window.h5glib.ReadyTask;
var ImageAnimator	= window.h5glib.ImageAnimator;
var Message			= window.h5glib.Message;
var MessageHandler	= window.h5glib.MessageHandler;
var MessageManager	= window.h5glib.MessageManager;
var SceneStatus		= window.h5glib.SceneStatus;
var Scene			= window.h5glib.Scene;


/**
 * RenderInfo
 */
var RenderInfo =
{
	loopInterval	: 50,
	chipPixels		: 64
};

/**
 * Res
 */
var Res =
{
	String :
	{
		HTML_USAGE	: "<p>" +
					  "[←][→][↑][↓]: 移動 [ESC]: メニューに戻る<br>" +
					  "ニダーが住民に放火するので、モナーを操作して消火します。<br>" +
					  "モナーが燃えている住民に近づくと消火します。<br>" +
					  "</p>"
	},
	Color :
	{
		BACKGROUND		: "forestgreen"
	},
	Font :
	{
		LARGE		: "bold 18px 'ＭＳ Ｐゴシック'",
		SMALL		: "bold 14px 'ＭＳ Ｐゴシック'"
	}
};

/**
 * MessageType
 */
var MessageType =
{
	NONE			: 0,
	RET_PREV_SCENE	: 1
};

/**
 * StageTask
 */
var StageTask = function( scene )
{
	this.command		= scene.command;	// input

	this.map			= null,
	this.getMapHeight	= function() { return this.map.length; };
	this.getMapWidth	= function() { return this.map[0].length; };
};
StageTask.prototype = new Task();

(function( pt )
{
	/**
	 * 
	 */
	pt.setData = function( scene, map )
	{
		this.map	= map;
	};

	/**
	 * 
	 */
	pt.update = function( scene )
	{
		var upd			= false;
		var msgManager	= scene.msgManager;

		// command
		if ( this.command.tbl.escape )
		{
			var message	= new Message( MessageType.RET_PREV_SCENE );
			msgManager.postBreakMessage( message );
		}
		return upd;
	};
	/**
	 * 
	 */
	pt.draw = function( scene )
	{
		var canvas		= scene.canvas;
		var context		= scene.context;
		var resource	= scene.data.resource;

		context.save();

		// 上書きモード
		context.globalCompositeOperation	= "source-over";
		context.fillStyle					= Res.Color.BACKGROUND;
		context.fillRect( 0, 0, canvas.width, canvas.height );

		context.restore();
	};
})( StageTask.prototype );

/**
 * ActorListTask
 */
var ActorListTask = function( scene )
{
	this.command	= scene.command;	// input
};
ActorListTask.prototype = new Task();

(function( pt )
{
	/**
	 * 
	 */
	pt.setData = function( scene, actors )
	{
		this.child	= null;

		// load actorList
		for ( var i = 0; i < actors.length; i++ )
		{
			var task	= new ActorTask();
			task.init( scene, i, actors[i] );
			// add
			if ( this.child == null )
			{
				this.child = task;
			}
			else
			{
				this.child.append( task );
			}
		}
	};
	/**
	 * 
	 */
	pt.update = function( scene )
	{
		var upd		= false;
		var sound	= scene.soundTask;

		if ( this.updateChildren( scene ) )
		{
			upd = true;
		}

		// update water
		var mona = this.child;
		for ( var actor = this.child.next.next; actor != null; actor = actor.next )
		{
			if ( actor.emitter.active )
			{
				var dx		= actor.ad.x - mona.ad.x;
				var dy		= actor.ad.y - mona.ad.y;
				var dist	= Math.sqrt( dx * dx + dy * dy );
				if ( dist < 1.2 )
				{
					sound.prepare("water");
					actor.emitter.active = false;
					upd = true;
				}
			}
		}
		// update fire
		var nida = this.child.next;
		for ( var actor = this.child.next.next; actor != null; actor = actor.next )
		{
			if ( !actor.emitter.active )
			{
				var dx		= actor.ad.x - nida.ad.x;
				var dy		= actor.ad.y - nida.ad.y;
				var dist	= Math.sqrt( dx * dx + dy * dy );
				if ( dist < 1.2 )
				{
					sound.prepare("fire");
					actor.emitter.active = true;
					upd = true;
				}
			}
		}
		return upd;
	};
	/**
	 * 
	 */
	pt.draw = function( scene )
	{
		this.drawChildren( scene );
	};
})( ActorListTask.prototype );

/**
 * ActorStatus
 */
var ActorStatus =
{
	STATIC	: 0,
	ACTIVE	: 1,
	BURNING	: 2
};

/**
 * ActorTask
 */
var ActorTask = function( scene )
{
	this.statusObjs	= null;
	this.animator	= null;
	this.id			= 0;
	// actor data
	this.ad			= null;
	this.velocity	= 0;

	this.emitter	= new ParticleEmitter();
};
ActorTask.prototype = new Task();

(function( pt )
{
	pt.STEP_VAL		= 0.3;

	/**
	 * 
	 */
	pt.init = function( scene, id, actor )
	{
		this.id		= id;
		this.ad		= actor;

		this.moveTo( scene.stageTask, this.ad.x, this.ad.y );
		this.command= ( id == 0 ) ? scene.command : new AICommand();

		var image		= scene.data.resource.image.actors[ this.ad.type ].data;
		var animation	= scene.data.animation;
		this.animator	= new ImageAnimator( image, animation.actorAction )

		this.setStatus( this.ad.status );

		// init emitter
		this.emitter.setPosition( this.ad.x * RenderInfo.chipPixels, this.ad.y * RenderInfo.chipPixels );
		if ( this.status != ActorStatus.BURNING )
		{
			this.emitter.active = false;
		}
	};
	/**
	 * 
	 */
	pt.setStatus = function( status )
	{
		this.status		= status;
	};
	/**
	 * 
	 */
	pt.moveTo = function( stage, x, y )
	{
		// restore map value
		stage.map[ Math.floor( this.ad.y ) ][ Math.floor( this.ad.x ) ] = 0;
		this.ad.x = x;
		this.ad.y = y;
		// update map value
		stage.map[ Math.floor( this.ad.y ) ][ Math.floor( this.ad.x ) ] = Config.actorValBase + this.id;
	};
	/**
	 * 
	 */
	pt.isInnerWall = function( stage, x, y )
	{
		var arr = [ -0.3, 0.3 ];

		for ( var i = 0; i < arr.length; i++ )
		{
			var my = Math.floor( y + arr[i] );
			if ( my < 0 || stage.getMapHeight() <= my ) { return true; }

			for ( var j = 0; j < arr.length; j++ )
			{
				var mx = Math.floor( x + arr[j] );
				if ( mx < 0 || stage.getMapWidth() <= mx ) { return true; }

				var value = stage.map[ my ][ mx ];
				if ( value != 0 && value != Config.actorValBase + this.id )
				{
					return true;
				}
			}
		}
		return false;
	};
	/**
	 * 
	 */
	pt.update = function( scene )
	{
		var upd = false;

		this.command.update();

		if ( this.velocity != 0 )
		{
			this.velocity /= 2;
			if ( this.velocity <= this.STEP_VAL / 4 )
			{
				this.velocity = 0;
			}
			upd = true;
		}
		else
		{
			// command
			if ( this.command.tbl.left )
			{
				this.velocity = this.STEP_VAL;
				this.ad.dir = 3;
			}
			else if ( this.command.tbl.right )
			{
				this.velocity = this.STEP_VAL;
				this.ad.dir = 1;
			}
			else if ( this.command.tbl.up )
			{
				this.velocity = this.STEP_VAL;
				this.ad.dir = 0;
			}
			else if ( this.command.tbl.down )
			{
				this.velocity = this.STEP_VAL;
				this.ad.dir = 2;
			}
		}

		// move
		if ( this.velocity != 0 )
		{
			var vx		= ( this.ad.dir == 1 ) ? this.velocity : ( this.ad.dir == 3 ) ? this.velocity * -1 : 0;
			var vy		= ( this.ad.dir == 2 ) ? this.velocity : ( this.ad.dir == 0 ) ? this.velocity * -1 : 0;
			var newX	= this.ad.x + vx;
			var newY	= this.ad.y + vy;

			if ( !this.isInnerWall( scene.stageTask, newX, newY ) )
			{
				this.moveTo( scene.stageTask, newX, newY );
				this.animator.start();
				this.emitter.setPosition( this.ad.x * RenderInfo.chipPixels, this.ad.y * RenderInfo.chipPixels );
				upd = true;
			}
		}
		// animation
		if ( this.animator.isActive() )
		{
			upd = this.animator.proceed();
		}
		else
		{
			var r = Math.floor( Math.random() * 10 );
			if ( r == 1 )
			{
				this.animator.start();
				upd = true;
			}
		}
		// update emitter
		this.emitter.update();

		return upd;
	};
	/**
	 * 
	 */
	pt.draw = function( scene )
	{
		var canvas		= scene.canvas;
		var context		= scene.context;
		var resource	= scene.data.resource;

		context.save();

		// 上書きモード
		context.globalCompositeOperation	= "source-over";

		var image = this.animator.image;
		context.drawImage(
			image,
			this.animator.fx,
			this.ad.dir * this.animator.fh,
			this.animator.fw,
			this.animator.fh,
			this.ad.x * RenderInfo.chipPixels - this.animator.fw / 2,
			this.ad.y * RenderInfo.chipPixels - this.animator.fh / 2,
			this.animator.fw,
			this.animator.fh
		);
		// draw emitter
		this.emitter.draw( context );

		context.restore();
	};
})( ActorTask.prototype );

/**
 * Vector
 */
var Vector = function( x, y )
{
	this.x = x || -1;
	this.y = y || -1;
};

(function( pt )
{
	/**
	 * 
	 */
	pt.multiply = function( scaleFactor )
	{
		this.x *= scaleFactor;
		this.y *= scaleFactor;
	};
	/**
	 * 
	 */
	pt.add = function( vector )
	{
		this.x += vector.x;
		this.y += vector.y;
	};
})( Vector.prototype );

/**
 * Particle
 */
var Particle = function()
{
	this.position		= new Vector();
	this.direction		= new Vector();
	this.radius			= 0;
	this.radiusSmall	= 0;
	this.timeToLive		= 0;
	this.color			= [];
	this.deltaColor		= [];
};

(function( pt )
{
	/**
	 * 
	 */
	pt.init = function( prop )
	{
		// timeToLive
		this.timeToLive	= prop.lifeSpan + this.getRandomValue( prop.lifeSpanRandom );
		// position
		this.position.x	= prop.position.x + this.getRandomValue( prop.positionRandom.x );
		this.position.y	= prop.position.y + this.getRandomValue( prop.positionRandom.y );
		// direction
		var newAngle	= prop.angle + this.getRandomValue( prop.angleRandom );
		var vector		= new Vector( Math.cos( newAngle ), Math.sin( newAngle ) );
		var vectorSpeed	= prop.speed + this.getRandomValue( prop.speedRandom );
		vector.multiply( vectorSpeed );
		this.direction	= vector;
		// radius
		this.radius		= prop.radius + this.getRandomValue( prop.radiusRandom );
		this.radius		= ( this.radius < 0 ) ? 0 : Math.floor( this.radius );
		// radiusSmall
		var sharpness	= prop.sharpness + this.getRandomValue( prop.sharpnessRandom );
		sharpness		= sharpness > 100 ? 100 : sharpness < 0 ? 0 : sharpness;
		this.radiusSmall= Math.floor( this.radius * sharpness / 100 );
		// color
		var start	= [];
		var finish	= [];
		for ( var i = 0; i < 4; i++ )
		{
			start[i]	= prop.startColor[i]  + this.getRandomValue( prop.startColorRandom[i] );
			finish[i]	= prop.finishColor[i] + this.getRandomValue( prop.finishColorRandom[i] );
			this.deltaColor[i]	= ( finish[i] - start[i] ) / this.timeToLive;
		}
	    this.color = start;
	};
	/**
	 * 
	 */
	pt.getRandomValue = function( rv )
	{
		return ( Math.random() * rv * 2 - rv );
	};
	/**
	 * 
	 */
	pt.update = function( prop )
	{
		this.timeToLive -= prop.delta;

		// position
		this.direction.add( prop.gravity );
		this.position.add( this.direction );

		// color
		this.color[ 0 ] += ( this.deltaColor[ 0 ] * prop.delta );
		this.color[ 1 ] += ( this.deltaColor[ 1 ] * prop.delta );
		this.color[ 2 ] += ( this.deltaColor[ 2 ] * prop.delta );
		this.color[ 3 ] += ( this.deltaColor[ 3 ] * prop.delta );
	};
	/**
	 * 
	 */
	pt.getColorString = function()
	{
		var r = this.color[ 0 ];
		var g = this.color[ 1 ];
		var b = this.color[ 2 ];
		var a = this.color[ 3 ];

		r = ( r > 255 ) ? 255 : ( r < 0 ) ? 0 : Math.floor( r );
		g = ( g > 255 ) ? 255 : ( g < 0 ) ? 0 : Math.floor( g );
		b = ( b > 255 ) ? 255 : ( b < 0 ) ? 0 : Math.floor( b );
		a = ( a >   1 ) ?   1 : ( a < 0 ) ? 0 : a.toFixed( 2 );

		return ("rgba(" + r + "," + g + "," + b + "," + a + ")");
	};
	/**
	 * 
	 */
	pt.draw = function( context )
	{
		var size	= this.radius * 2;
		var x		= Math.floor( this.position.x );
		var y		= Math.floor( this.position.y );

		var rg		= context.createRadialGradient( x, y, this.radiusSmall, x, y, this.radius );
		rg.addColorStop( 0, this.getColorString() );
		rg.addColorStop( 1, 'rgba(0,0,0,0)' );

		context.fillStyle	= rg;
		context.fillRect( x - this.radius, y - this.radius, size, size );
	};
})( Particle.prototype );

/**
 * ParticleEmitter
 */
var ParticleEmitter = function()
{
	this.particles		= [];
	this.active			= true;

	// Properties
	this.prop	=
	{
		maxParticles		: 20,
		delta				: 1,
		gravity				: new Vector( 0.2, -0.4 ),
		position			: new Vector( 200, 200 ),	// pixxel
		positionRandom		: new Vector( 10, 10 ),
		radius				: 20,	// pixel
		radiusRandom		: 10,
		speed				: 4,	// pixel
		speedRandom			: 1.5,
		lifeSpan			: 8,	// frame
		lifeSpanRandom		: 5,
		angle				: 0,	// radian
		angleRandom			: Math.PI * 2,
		startColor			: [ 224, 192, 128, 1 ],	// r, g ,b, a
		startColorRandom	: [  64,  64,  64, 0 ],
		finishColor			: [ 245,  32,   0, 0 ],
		finishColorRandom	: [  64,  64,  64, 0 ],
		sharpness			: 40,	// percentage
		sharpnessRandom		: 10
	};
};

(function( pt )
{
	/**
	 * 
	 */
	pt.setPosition = function( x, y )
	{
		this.prop.position.x	= x;
		this.prop.position.y	= y;
	};
	/**
	 * 
	 */
	pt.update = function()
	{
		var upd = false;

		// add particle
		if ( this.active )
		{
			var emissionRate	= this.prop.maxParticles / this.prop.lifeSpan;
			var count			= 0;
			while ( this.particles.length < this.prop.maxParticles &&
					count < emissionRate )
			{
				// add
				var particle	= new Particle();
				particle.init( this.prop );
				this.particles.push( particle );

				count++;
			}
		}
		// update particle
		var i = 0;
		while ( i < this.particles.length )
		{
			// check life time
			if ( this.particles[i].timeToLive > 0 )
			{
				this.particles[i].update( this.prop );
				i++;
			}
			else
			{
				this.particles.splice( i, 1 );
			}
			upd = true;
		}
		return upd;
	};
	/**
	 * 
	 */
	pt.draw = function( context )
	{
		// ブレンドモード
		context.globalCompositeOperation	= "lighter";
		// draw particles
		for( var i = 0; i < this.particles.length; i++ )
		{
			this.particles[i].draw( context );
		}
	};
})( ParticleEmitter.prototype );

/**
 * DebugTask
 */
var DebugTask = function( scene )
{
	this.command	= scene.command;	// input
	this.info		= null;
	this.lag		= 0;
};
DebugTask.prototype = new Task();

(function( pt )
{
	pt.LAG_VAL	= 10;

	/**
	 * 
	 */
	pt.update = function( scene )
	{
		var upd = false;
		// lag
		if ( this.lag )
		{
			this.lag--;
		}
		else
		{
			if ( this.command.tbl.debug )
			{
				this.info	= "scene.particleTask.running=" + scene.particleTask.running;
				this.lag	= this.LAG_VAL;
				upd = true;
			}
		}
		return upd;
	};

	/**
	 * 
	 */
	pt.draw = function( scene )
	{
		// output debug info
		if ( this.info )
		{
			Debug.print( this.info );
			this.info = null;
		}
	};
})( DebugTask.prototype );

/**
 * AICommand
 */
var AICommand = function()
{
	this.tbl.left	= 0;
	this.tbl.right	= 0;
	this.tbl.up		= 0;
	this.tbl.down	= 0;
};
AICommand.prototype = new Command();

(function( pt )
{
	/**
	 * 
	 */
	pt.update = function()
	{
		this.clear();

		var r = Math.floor( Math.random() * 18 );
		if      ( 0 <= r && r <= 3 )	{ this.tbl.left		= 1; }
		else if ( 4 <= r && r <= 8 )	{ this.tbl.right	= 1; }
		else if ( r == 9 )				{ this.tbl.up		= 1; }
		else if ( r == 10 )				{ this.tbl.down		= 1; }
	};
})( AICommand.prototype );
/**
 * InputCommand
 */
var InputCommand = function()
{
	this.tbl.up		= 0;
	this.tbl.down	= 0;
	this.tbl.left	= 0;
	this.tbl.right	= 0;
	this.tbl.voice	= 0;
	this.tbl.escape	= 0;
	this.tbl.debug	= 0;
};
InputCommand.prototype = new Command();

(function( pt )
{
	/**
	 * 
	 */
	pt.update = function() {};
	/**
	 * 
	 */
	pt.handleSysEvent = function( event )
	{
		var type = event.type.toLowerCase();
		if ( type.substring( 0, 3 ) == "key" )
		{
			var value = 0;
			if      ( type == "keydown" ) { value = 1; }
			else if ( type == "keyup"   ) { value = 0; }

			switch ( event.keyCode )
			{
				case 37: this.tbl.left	= value; break;	// ←
				case 39: this.tbl.right	= value; break;	// →
				case 38: this.tbl.up	= value; break;	// ↑
				case 40: this.tbl.down	= value; break;	// ↓
				case 90: this.tbl.voice	= value; break;	// Z
				case 27: this.tbl.escape= value; break;	// ESC
				case 81: this.tbl.debug	= value; break;	// Q
			}
		}
	};
})( InputCommand.prototype );

/**
 * FiremanScene
 */
var FiremanScene = function( app, name )
{
	this.app			= app;
	this.name			= name;
	this.data			= null;
	this.command		= new InputCommand();

	// create task
	this.readyTask		= new ReadyTask( this );
	this.stageTask		= new StageTask( this );
	this.actorListTask	= new ActorListTask( this );
	this.soundTask		= new SoundTask( this );
	this.debugTask		= new DebugTask( this );
	// create list
	this.stageTask.append( this.actorListTask );
	this.stageTask.append( this.soundTask );
	this.stageTask.append( this.debugTask );

	this.headTasks		=
	[
		this.readyTask,
		this.stageTask,
		this.readyTask
	];
	this.setStatus( SceneStatus.READY );

	// message handler
	this.msgManager		= new MessageManager();
	this.msgManager.addHandler( new MessageHandler( MessageType.RET_PREV_SCENE,	this, this.handleRetPrevScene ) );
};
FiremanScene.prototype = new Scene();

(function( pt )
{
	/**
	 * 
	 */
	pt.handleRetPrevScene = function( scene, message )
	{
		// restore interval
		this.app.setInterval( Config.loopInterval );

		this.app.sceneManager.pop();
		var prevScene = this.app.sceneManager.current;
		prevScene.show();
	};
	/**
	 * 
	 */
	pt.setStatus = function( status )
	{
		this.status	= status;
		this.child	= this.headTasks[ this.status ];
	};

	/**
	 * 
	 */
	pt.init = function()
	{
		// window.onload のタイミング
	};
	/**
	 * 
	 */
	pt.show = function()
	{
		// set interval
		this.app.setInterval( RenderInfo.loopInterval );

		this.setUsage( Res.String.HTML_USAGE );

		this.command.clear();
		this.holdCanvas();
		this.drawChildren( this );
	};

	/**
	 * 
	 */
	pt.setData = function( data )
	{
		this.data	= data;

		this.stageTask.setData( this, data.map );
		this.actorListTask.setData( this, data.actors );
	};
	/**
	 * 
	 */
	pt.getData = function()
	{
		return this.data;
	};
	/**
	 * 
	 */
	pt.loadData = function( data )
	{
		try
		{
			// load resource
			this.app.loadResource( this.name, data.resource );
			// set data
			this.setData( data );
			// set status
			var self = this;
			window.setTimeout( function() { self.setStatus( SceneStatus.RUNNING ); self.show(); }, 500 );
		}
		catch ( e )
		{
			this.app.kill();
			Debug.alertError( e );
		}
	};
})( FiremanScene.prototype );


// Expose
if ( !window.h5glib ) { window.h5glib = {}; }
window.h5glib.FiremanScene	= FiremanScene;

// Anonymous function end
//
})( window );
