
//
//ゲームステージ
//

function GameStage(){
	//****コンストラクタ****
	//**インスタンス変数宣言・初期化**
	//タイマーカウントを初期化
	this.tickCount = 0;
	//GameManagerから渡されるプロパティ
	this.manager = null;
	this.mainCanvas = null;
	this.debugCanvas = null;
	this.mainContext = null;
	this.debugContext = null;
	//StageObjectのリスト
	this.stageObjectList = new Array();
	this.stageObjectLinearList = new Array();
	this.globalStageObjectList = new Array();
	this.pendingGlobalStageObjectData = new RequestData();
	this.pendingGlobalStageObjectList = new Array();
	//衝突判定用canvas
	this.collisionMapCanvas = null;
	this.collisionMapContext = null;
	this.debugMode = false;
	//背景の画像のimgエレメントオブジェクトを指定
	this.background = null;
	//ユーザーが操作しているキャラクターオブジェクト
	this.userControlledCharacter = null;
	//水面のy座標。0で無効
	this.waterLevel = 0;
	this.gravity = 700 / mainManager.tickPerSecond;
	this.waterGravity = 700 / mainManager.tickPerSecond;
	this.friction = 300 / mainManager.tickPerSecond;
	this.waterFriction = 600 / mainManager.tickPerSecond;
}
GameStage.prototype = {
	//以下の関数をオーバーライドしてステージを作成する。
	keyDown: function(event){
		//キー入力
	},
	keyUp: function(event){
		//キー入力
	},
	timerTick: function(){
		//タイマー
		this.tickCount++;
		//全てのオブジェクトを計算
		for(var sp in this.stageObjectList)
		{
			var tickAllObjects = true;
			//まず、クラス定義.tick() (例えばBlockStageObjectClass.tick = function(){};) が定義されてたら実行
			if(sp.tick){
				//クラスspのtickの戻り値は、このクラスのオブジェクトのインスタンスのtickを実行する必要があるかどうか
				tickAllObjects = sp.tick(this.stageObjectList[sp]);
			}
			
			//次に各種StageObjectのtickを実行
			if(tickAllObjects)
			{
				for(var i = 0, i_max = this.stageObjectList[sp].length; i < i_max; i++){
					var o = this.stageObjectList[sp][i];
					if(o instanceof MovableStageObjectClass){
						var g = this.gravity - o.antigravity;
						var f = this.friction;
						var h = f * 2;
						//共通運動処理
						if(this.waterLevel && this.waterLevel <= o.origin.y + o.size.y){
							//水中
							o.inWater = true;
							g = this.waterGravity - o.antigravity;
							f = this.waterFriction;
							var h = f * 4;
							if(o.movingSpeed.y < -f){
								o.movingSpeed.y += f;
							} else if(o.movingSpeed.y > f){
								o.movingSpeed.y -= f;
							} else{
								o.movingSpeed.y = 0;
							}
							o.movingSpeed.y += g;
							if(o.movingSpeed.x < -f){
								o.movingSpeed.x += f;
							} else if(o.movingSpeed.x > f){
								o.movingSpeed.x -= f;
							} else{
								o.movingSpeed.x = 0;
							}
						} else{
							//大気中
							o.inWater = false;
							o.movingSpeed.y += g;
							if(o.movingSpeed.x < -h){
								o.movingSpeed.x += f;
							} else if(o.movingSpeed.x > h){
								o.movingSpeed.x -= f;
							} else{
								o.movingSpeed.x = 0;
							}
						}
					}
				}
				//ここはループ終了値をlengthで直接取得しなければいけない（削除されるオブジェクトがある可能性があるため）
				for(var i = 0; i < this.stageObjectList[sp].length; i++){
					var o = this.stageObjectList[sp][i];
					//各オブジェクト処理
					if(o.tick()){
						return;
					}
				}
			}
		}
		// 操作中キャラの移動処理
		var c = this.userControlledCharacter;
		if(c)
		{
			if(this.manager.UIManager.keyState.jump)
			{
				if(c.onLadder){
					//はしご上る
					c.movingSpeed.y = 0;
					c.moveTo(c.origin.x, c.origin.y - 4);
				} else{
					//ジャンプ
					c.jump();
				}
			} else if(c.jumpEnd){
				if(!c.onLadder){
					//ジャンプ後の処理
					c.jumpEnd();
				}
			}
			if(!(this.manager.UIManager.keyState.goLeft && this.manager.UIManager.keyState.goRight))
			{
				//左右
				if(this.manager.UIManager.keyState.goLeft){
					c.goLeft();
				}
				if(this.manager.UIManager.keyState.goRight){
					c.goRight();
				}
			}
			if(this.manager.UIManager.keyState.fire){
				c.fire();
			}
		}
	},
	draw: function(){
		this.drawBackground();
		this.drawAsPoint(0, 0);
		if(this.waterLevel){
			this.drawWater();
		}
	},
	drawAsPoint: function(x, y){
	
		// 表示の重ね合わせ順 : 他の項目 < キャラ < forceTopMost==trueの項目
	
		var characterList = [];
		var topList = [];
		var drawItem = function(stgobj)
		{
			var px = stgobj.origin.x - x, py = stgobj.origin.y - y;
			
			if(px > -stgobj.size.x && py > -stgobj.size.y && px < 640 && py < 480)
			{
				stgobj.draw(px, py);
				if(this.debugMode){
					stgobj.debugDraw(px, py);
				}
			}
		};
		
		//すべてのオブジェクトを再描画
		//x, yは描画位置のオフセット
		for(var sp in this.stageObjectList)
		{
			//各種StageObjectのdrawを実行
			for(var i = 0, li = this.stageObjectList[sp].length; i < li; i++){
				var stgobj = this.stageObjectList[sp][i];
				if(stgobj.forceTopMost)
				{
					topList.push(stgobj);
				}else if(stgobj instanceof CharacterClass)
				{
					characterList.push(stgobj);
				}else
				{
					drawItem(stgobj);
				}
			}
		}
		
		for(var i in characterList)
		{
			var stgobj = characterList[i];
			drawItem(stgobj);
		}
		
		for(var i in topList)
		{
			var stgobj = topList[i];
			drawItem(stgobj);
		}
	},
	drawBackground: function(){
		if(this.background)
		{
			this.mainContext.drawImage(this.background, 0, 0);
			
		} else{
			//キャンバスを全消去
			this.mainContext.clearRect(0, 0, this.mainCanvas.width, this.mainCanvas.height);
		}
		if(this.collisionMapContext)
		{
			this.collisionMapContext.clearRect(0, 0, this.collisionMapCanvas.width, this.collisionMapCanvas.height);
		}
	},
	drawWater: function(){
		//水部分を描画する。見た目だけ
		this.mainContext.fillStyle = "rgba(100,128,255, 0.32)";
		fillRect(this.mainContext, 0, this.waterLevel, this.mainCanvas.width, this.mainCanvas.height - this.waterLevel);
	},
	runStage: function(){
		//ステージ初期化処理
		this.tickCount = 0;
		//衝突マップ初期化
		this.collisionMapCanvas = createCanvas("collisionMapCanvas", this.mainCanvas.width, this.mainCanvas.height, this.mainCanvas.width, 0, 1, this.manager.mainArea);
		this.collisionMapContext = this.collisionMapCanvas.getContext('2d');
		this.collisionMapContext.fillStyle = "rgba(0,0,0, 0.2)";
		this.collisionMapContext.strokeStyle = "rgba(0,0,0, 0.2)";
	},
	stopStage: function(){
		//ステージ終了処理
		destroyDOMObjectByID(this.collisionMapCanvas.id);
		this.collisionMapCanvas = null;
		this.collisionMapContext = null;
		this.stageObjectList = new Array();
		this.stageObjectLinearList = new Array();
		this.globalStageObjectList = new Array();
	},
	addStageObject: function(aStageObject, isGlobalObject, isFromNetwork){
		//stageObjectをリストに追加する
		//isGlobalObject:ネットワーク更新が必要な場合trueを渡す。不要な場合は省略できる。
		//stageObjectListはオブジェクトのクラスごとの連想配列になっている。
		var sp = aStageObject.constructor;
		if(this.stageObjectList[sp] == undefined)
		{
			this.stageObjectList[sp] = [];
		}
		this.stageObjectList[sp].push(aStageObject);
		this.stageObjectLinearList.push(aStageObject);
		if(isGlobalObject){
			if(!isFromNetwork){
				this.pendingGlobalStageObjectData.append(this.pendingGlobalStageObjectData.length, aStageObject.getSyncData());
				this.pendingGlobalStageObjectList.push(aStageObject);
			} else{
				this.globalStageObjectList.push(aStageObject);
			}
		}
	},
	removeStageObject: function(aStageObject){
		//stageObjectをリストから削除する
		var sp = aStageObject.constructor;
		if(sp in this.stageObjectList)
		{
			removeObjectFromArray(this.stageObjectList[sp], aStageObject);
			if(this.stageObjectList[sp].length == 0)
			{
				//空になったクラスの配列は削除する
				removeObjectFromArray(this.stageObjectList, sp);
			}
		}
		removeObjectFromArray(this.stageObjectLinearList, aStageObject);
		//globalStageObjectListから削除。存在しない場合は何も起きないのでこれで大丈夫
		removeObjectFromArray(this.globalStageObjectList, aStageObject);
	},
	eachStageObject: function(f){
		//リスト上のすべてのオブジェクト一つずつを引数として関数fをそれぞれ実行する。
		//関数fがfalseを返した場合、そこで実行を中断してfalseを返す。
		for(var sp in this.stageObjectList)
		{
			for(var i = 0; i < this.stageObjectList[sp].length; i++){
				
				var stgobj = this.stageObjectList[sp][i];
				if(!f(stgobj)) return false;
			}
		}
		return true;
	},
	eachStageObjectById: function(id, f){
		for(var sp in this.stageObjectList)
		{
			for(i = 0; i < this.stageObjectList[sp].length; i++){
				
				var stgobj = this.stageObjectList[sp][i];
				if(stgobj.id == id)
				{
					if(!f(stgobj)) return false;
				}
			}
		}
		return true;
	},
	eachOwnStageObject: function(f){
		//自分自身が所有するオブジェクトすべてに対して関数fを適用する
		for(var sp in this.stageObjectList){
			for(var i = 0; i < this.stageObjectList[sp].length; i++){
				var stgobj = this.stageObjectList[sp][i];
				if(stgobj.ownerUID == this.manager.userID){
					f(stgobj);
				}
			}
		}
	},
	getGlobalStageObject: function(objid){
		for(var i = 0; i < this.globalStageObjectList.length; i++){
			if(objid == this.globalStageObjectList[i].objectID){
				return this.globalStageObjectList[i];
			}
		}
		return null;
	},
	appendSyncDataTo: function(data){
		for(var i = 0; i < this.globalStageObjectList.length; i++){
			var anObj = this.globalStageObjectList[i];
			if(anObj.ownerUID == this.manager.userID){
				anObj.bindAttribute();
				data.append(i, anObj.objectID + "|" + anObj.origin.x + "|" + anObj.origin.y + "|" + anObj.movingSpeed.x + "|" +  anObj.movingSpeed.y+ "|" + parseArrayToStringSource(anObj.attribute));
			}
		}
	},
};

