/*
 * 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;

/**
 * Res
 */
var Res =
{
	String :
	{
		HTML_USAGE	: "<p>" +
					  "＜キー入力ができない場合はゲームの画面をクリック＞<br>" +
					  "[←][→]: 移動, [↑][↓]: 速度変更" +
					  "</p>"
	},
	Color :
	{
		SKY			: "dodgerblue",
		GROUND		: "forestgreen",
		ROAD		: "dimgray",
		CENTER_LINE	: "rgb(224, 224, 224)"
	},
	Font :
	{
		LARGE		: "bold 18px 'ＭＳ Ｐゴシック'",
		SMALL		: "bold 14px 'ＭＳ Ｐゴシック'"
	}
};

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

	this.map		= new Array( 20 );
};
StageTask.prototype = new Task();

(function( pt )
{
	pt.ROAD_HEIGHT		= 150;
	pt.RENDER_LENGTH	= 10;
	pt.RENDER_OFF		= 2;
	pt.CURVE_RAD		= 0.1;

	/**
	 * 
	 */
	pt.init = function( scene )
	{
		// clear
		for ( var i = 0; i < this.map.length; i++ )
		{
			this.map[i] = 0;
		}
		// set data
		for ( var i = 0; i < this.map.length; i++ )
		{
			this.setMapData( i );
		}
	};

	/**
	 * 
	 */
	pt.setMapData = function( index )
	{
		// get previous sequence
		var idx = index - 1;
		if ( idx < 0 ) { idx = this.map.length - 1; }
		var pval	= this.map[ idx ];
		var count	= 1;

		for ( var i = 0; i < this.map.length - 2; i++ )
		{
			idx--;
			if ( idx < 0 ) { idx = this.map.length - 1; }

			if ( pval == this.map[ idx ] )
			{
				count++;
			}
			else
			{
				break;
			}
		}

		// set data
		if ( count < 10 )
		{
			this.map[ index ] = pval;
		}
		else if ( count >= 15 || Math.random() > 0.5 )
		{
			if ( pval == 0 )
			{
				this.map[ index ] = ( Math.random() > 0.5 ) ? 1 : -1;
			}
			else
			{
				this.map[ index ] = 0;
			}
		}
	};
	/**
	 * 
	 */
	pt.addPosition = function( pos, add, updateMap )
	{
		var newPos = pos + add;
		if ( newPos >= this.map.length )
		{
			newPos -= this.map.length;
		}

		if ( updateMap )
		{
			var oldPosN = Math.floor( pos );
			var newPosN = Math.floor( newPos );
			while ( oldPosN != newPosN )
			{
				this.setMapData( oldPosN );
				oldPosN = ( oldPosN + 1 ) % this.map.length;
			}
		}
		return newPos;
	};

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

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

		context.save();

		// sky
		var roadTop	= canvas.height - this.ROAD_HEIGHT;
		var grad;

		grad	= context.createLinearGradient( 0, 0, 0, roadTop );
		grad.addColorStop( 0, 'rgb( 16,  32, 64)' );
		grad.addColorStop( 1, 'rgb(160, 192, 255)' );
		context.fillStyle	= grad;

		context.beginPath();
		context.rect( 0, 0, canvas.width, roadTop );
		context.fill();

		// ground
		grad	= context.createLinearGradient( 0, roadTop, 0, this.ROAD_HEIGHT );
		grad.addColorStop( 0, 'rgb(192, 255, 192)' );
		grad.addColorStop( 1, 'rgb(128, 192,128)' );
		context.fillStyle	= grad;

		context.beginPath();
		context.rect( 0, roadTop, canvas.width, this.ROAD_HEIGHT );
		context.fill();

		// road
		var centerX			= canvas.width  / 2;
		var roadWidthHalf	= canvas.width  / 2;
		var lineWidthHalf	= 10;

		var arr	= this.getRoadPoints( scene );
		for ( var i = 0; i < arr.length - 1; i++ )
		{
			var x1	= Math.floor( centerX + ( roadWidthHalf * arr[i].x / this.RENDER_LENGTH ) );
			var op1	= ( this.RENDER_LENGTH - arr[i].y ) / this.RENDER_LENGTH;
			op1 *= op1;
			var y1	= Math.floor( roadTop + ( this.ROAD_HEIGHT * op1 ) );
			var rw1	= Math.floor( roadWidthHalf * op1 );
			var lw1	= Math.floor( lineWidthHalf * op1 );

			var x2	= Math.floor( centerX + ( centerX * arr[i+1].x / this.RENDER_LENGTH ) );
			var op2	= ( this.RENDER_LENGTH - arr[i+1].y ) / this.RENDER_LENGTH;
			op2 *= op2;
			var y2	= Math.floor( roadTop + ( this.ROAD_HEIGHT * op2 ) );
			var rw2	= Math.floor( roadWidthHalf * op2 );
			var lw2	= Math.floor( lineWidthHalf * op2 );

			// draw road
			context.fillStyle	= Res.Color.ROAD;
			context.beginPath();
			context.moveTo( x1 - rw1, y1 );
			context.lineTo( x2 - rw2, y2 );
			context.lineTo( x2 + rw2, y2 );
			context.lineTo( x1 + rw1, y1 );
			context.lineTo( x1 - rw1, y1 );
			context.fill();

			// draw line
			var pos = Math.floor( viewPoint.ad.y ) - this.RENDER_OFF;
			if ( pos < 0 ) { pos = this.map.length - 1 + pos; }

			if ( ( pos + i ) % 2 == 0 )
			{
				context.fillStyle	= Res.Color.CENTER_LINE;
				context.beginPath();
				context.moveTo( x1 - lw1, y1 );
				context.lineTo( x2 - lw2, y2 );
				context.lineTo( x2 + lw2, y2 );
				context.lineTo( x1 + lw1, y1 );
				context.lineTo( x1 - lw1, y1 );
				context.fill();
			}
		}
		// character
		for ( var i = 0; i < visible.length; i++ )
		{
			var actor	= visible[i];
			var idx		= Math.floor( actor.dist ) + this.RENDER_OFF;

			var dx		= Math.floor( centerX + ( roadWidthHalf * arr[ idx ].x / this.RENDER_LENGTH ) );
			var opr		= ( this.RENDER_LENGTH - ( actor.dist + this.RENDER_OFF ) ) / this.RENDER_LENGTH;
			op1 *= opr;
			var dy		= Math.floor( roadTop + ( this.ROAD_HEIGHT * opr ) );
			var xoff	= Math.floor( ( roadWidthHalf * actor.ad.x / this.RENDER_LENGTH ) * opr );
			var dw		= Math.floor( actor.image.width  * opr );
			var dh		= Math.floor( actor.image.height * opr );

			context.drawImage(
				actor.image,
				0,							// sx
				0,							// sy
				actor.image.width,			// sw
				actor.image.height,			// sh
				dx + xoff - ( dw / 2 ),		// dx
				dy - ( dh / 2 ),			// dy
				dw,							// dw
				dh							// dh
			);
		}
		context.restore();
	};

	/**
	 * 
	 */
	pt.getRoadPoints = function( scene )
	{
		var viewPoint	= scene.viewPoint;

		var arr		= [];
		var pos		= Math.floor( viewPoint.ad.y );
		var diff	= 1 - ( viewPoint.ad.y - pos );
		pos -= this.RENDER_OFF;
		if ( pos < 0 ) { pos = this.map.length - 1 + pos; }

		var rad		= 0;
		var sign	= 1;

		var xp		= 0;
		var yp		= 0;
		arr.push( { x : xp, y : yp } );

		for ( var i = 0; i < this.map.length; i++ )
		{
			var val		= this.map[ pos % this.map.length ];
			pos++;

			if ( val != 0 )
			{
				if ( rad == 0 )
				{
					rad = this.CURVE_RAD / 2;
					if ( i == 0 ) { rad *= diff; }
				}
				else
				{
					rad += this.CURVE_RAD;
				}
				sign	= val;
			}

			xp += Math.sin( rad ) * sign;
			if ( i == 0 )
			{
				yp += diff;
			}
			else
			{
				yp += Math.cos( rad );
			}
			if (  yp >= this.RENDER_LENGTH ) { yp = this.RENDER_LENGTH; }

			arr.push( { x : xp, y : yp } );

			if (  yp >= this.RENDER_LENGTH ) { break; }
		}
		return arr;
	};
})( StageTask.prototype );

/**
 * ActorListTask
 */
var ActorListTask = function( scene )
{
	this.command	= scene.command;	// input
	this.visible	= [];
};
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( scene );
			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 stage		= scene.stageTask;
		var renderLen	= stage.RENDER_LENGTH - stage.RENDER_OFF;
		var mapLen		= stage.map.length;

		upd = this.updateChildren( scene );

		this.visible.length = 0;
		this.visible.push( this.child );
		for ( var actor = this.child.next; actor != null; actor = actor.next )
		{
			var dist = actor.ad.y - this.child.ad.y;
			if ( dist < 0 )
			{
				dist = mapLen - this.child.ad.y + actor.ad.y;
			}
			if ( dist < renderLen )
			{
				actor.dist = dist;
				this.visible.push( actor );
			}
		}
		this.visible.sort( function( a, b ) { return b.dist - a.dist;  } );
		return upd;
	};
})( ActorListTask.prototype );

/**
 * ActorTask
 */
var ActorTask = function( scene )
{
	this.id				= 0;
	this.ad				= null;
	this.image			= null;
	this.velocity		= this.VEL_DEF;
	this.dist			= 0;

	this.lag			= this.LAG_VAL;
};
ActorTask.prototype = new Task();

(function( pt )
{
	pt.LAG_VAL		= 5;
	pt.VEL_DEF		= 0.2;
	pt.VEL_ADD		= 0.05;
	pt.VEL_MAX		= 0.3;
	pt.STEP_X		= 1;
	pt.STEP_SKIP	= 2;

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

		this.command	= ( id == 0 ) ? scene.command : new AICommand();
		this.image		= scene.data.resource.image.actors[ this.ad.type ].data;
	};

	/**
	 * 
	 */
	pt.update = function( scene )
	{
		var upd = false;
		var context		= scene.context;
		var canvas		= context.canvas;
		var renderLen	= scene.stageTask.RENDER_LENGTH;

		this.command.update();

		// update position
		if ( scene.ticks % this.STEP_SKIP == 0 )
		{
			this.ad.y = scene.stageTask.addPosition( this.ad.y, this.velocity, ( this.id == 0 ) );
			upd = true;
		}

		if ( this.lag > 0 )
		{
			this.lag--;
		}
		else
		{
			// update action
			if ( this.command.tbl.up )
			{
				if ( this.velocity < this.VEL_MAX )
				{
					this.velocity += this.VEL_ADD;
					this.lag	= this.LAG_VAL;
					upd = true;
				}
			}
			else if ( this.command.tbl.down )
			{
				if ( this.velocity > this.VEL_DEF )
				{
					this.velocity -= this.VEL_ADD;
					this.lag	= this.LAG_VAL;
					upd = true;
				}
			}
			else if ( this.command.tbl.left )
			{
				if ( this.ad.x - this.STEP_X > renderLen * -1 )
				{
					this.ad.x -= this.STEP_X;
					this.lag	= this.LAG_VAL;
					upd = true;
				}
			}
			else if ( this.command.tbl.right )
			{
				if ( this.ad.x + this.STEP_X < renderLen )
				{
					this.ad.x += this.STEP_X;
					this.lag	= this.LAG_VAL;
					upd = true;
				}
			}
		}
		return upd;
	};
})( ActorTask.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.actorTask.status=" + scene.actorTask.status;
				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.up		= 0;
	this.tbl.left	= 0;
	this.tbl.down	= 0;
	this.tbl.right	= 0;
};
AICommand.prototype = new Command();

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

		var r = Math.floor( Math.random() * 12 );
		if      ( r == 0 )	{ this.tbl.up		= 1; }
		else if ( r == 1 )	{ this.tbl.left		= 1; }
		else if ( r == 2 )	{ this.tbl.down		= 1; }
		else if ( r == 3 )	{ this.tbl.right	= 1; }
	};
})( AICommand.prototype );

/**
 * InputCommand
 */
var InputCommand = function()
{
	this.tbl.up		= 0;
	this.tbl.left	= 0;
	this.tbl.down	= 0;
	this.tbl.right	= 0;

	this.tbl.enter	= 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 38: this.tbl.up	= value; event.prevent( event.orgEvent ); break;	// ↑
				case 37: this.tbl.left	= value; event.prevent( event.orgEvent ); break;	// ←
				case 40: this.tbl.down	= value; event.prevent( event.orgEvent ); break;	// ↓
				case 39: this.tbl.right	= value; event.prevent( event.orgEvent ); break;	// →

				case 90: this.tbl.enter	= value; break;	// Z
				case 27: this.tbl.escape= value; break;	// ESC
				case 81: this.tbl.debug	= value; break;	// Q
			}
		}
	};
})( InputCommand.prototype );

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

	// create task
	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 );
	// head of task list
	this.child			= this.stageTask;
	this.setStatus( SceneStatus.READY );

	// message handler
	//this.msgManager		= new MessageManager();
};
IconRaceScene.prototype = new Scene();

(function( pt )
{
	/**
	 * 
	 */
	pt.init = function()
	{
		// window.onload のタイミング
	};
	/**
	 * 
	 */
	pt.show = function()
	{
		this.setUsage( Res.String.HTML_USAGE );

		this.command.clear();
		this.holdContext();
		this.draw( this );
	};
	/**
	 * 
	 */
	pt.setData = function( data )
	{
		this.data		= data;

		// init stage
		this.stageTask.init( this );
		// load actorList
		this.actorListTask.setData( this, data.actors );
		// init viewPoint
		this.viewPoint	= this.actorListTask.child;
	};
	/**
	 * 
	 */
	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(); }, Config.loadInterval );
		}
		catch ( e )
		{
			this.app.kill();
			Debug.alertError( e );
		}
	};
})( IconRaceScene.prototype );


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

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