
//
//サブマネージャークラス
//
//これらのクラスとメインマネージャーは直接プロパティを参照し合うので注意

function NetworkManager(mManager){
	this.timeStamp = 0;
	this.syncIntervalMs = 128;
	if(mManager){
		window.setInterval(this.syncTimerTickEventListener, this.syncIntervalMs);
	}
}
NetworkManager.prototype = {
	//from http://hakuhin.jp/js/xmlhttprequest.html
	CreateRequestObject: function(){
		var rq = null;
		// XMLHttpRequest
		try{
			// XMLHttpRequest オブジェクトを作成
			rq = new XMLHttpRequest();
		}catch(e){}
		// Internet Explorer
		try{
			rq = new ActiveXObject('MSXML2.XMLHTTP.6.0');
		}catch(e){}
		try{
			rq = new ActiveXObject('MSXML2.XMLHTTP.3.0');
		}catch(e){}
		try{
			rq = new ActiveXObject('MSXML2.XMLHTTP');
		}catch(e){}
		if(rq == null){
			return null;
		}
		return rq;
	},
	RequestObjectDisableCache: function(rq){
		//call after open request.
		//disable cache
		//http://vird2002.s8.xrea.com/javascript/XMLHttpRequest.html
		rq.setRequestHeader('Pragma', 'no-cache');				// HTTP/1.0 における汎用のヘッダフィールド
		rq.setRequestHeader('Cache-Control', 'no-cache');		// HTTP/1.1 におけるキャッシュ制御のヘッダフィールド
		rq.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');
		
	},
	sendRequestSync: function(mode, url, data){
		//同期モード
		var q = this.CreateRequestObject();
		q.open(mode, url, false);
		q.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		this.RequestObjectDisableCache(q);
		q.send(data);
		if(q.status == 0){
			alert("ネットワークにアクセスできません。" + q.status + ":" + q.statusText);
		}else if((200 <= q.status && q.status < 300) || (q.status == 304)){
			var res = q.responseText;
			if(isValidResponseText(res)){
				return res;
			}
		}else{
			alert("サーバーがエラーを返しました。" + request.status + ":" + request.statusText);
		}
		return null;
	},
	sendRequestAsync: function(mode, url, data, callback){
		//非同期モード
		//callback(res);
		var q = this.CreateRequestObject();
		var that = this;
		q.onreadystatechange = function(){
			if(q.readyState == 4){
				if(q.status == 0){
					alert("ネットワークにアクセスできません。" + q.status + ":" + q.statusText);
				}else if((200 <= q.status && q.status < 300) || (q.status == 304)){
					var res = q.responseText;
					if(isValidResponseText(res)){
						callback(res);
					}
				}else{
					alert("サーバーがエラーを返しました。" + request.status + ":" + request.statusText);
				}
			}
		};
		q.open(mode, url, true);
		q.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		this.RequestObjectDisableCache(q);
		q.send(data);
	},
	sync: function(){
		//ネットワーク同期
		if(mainManager.userID != 0){
			var s = mainManager.runningStage;
			if(s && s.pendingGlobalStageObjectList.length > 0){
				console.log("Network:Send request to add " + s.pendingGlobalStageObjectList.length + " own object(s).");
				//サーバーにオブジェクト追加情報をアップロード
				//同期モード
				var res = this.sendRequestSync("POST", URL_PCD_Root + "update.php?uid=" + mainManager.userID + "&action=add", s.pendingGlobalStageObjectData.data);
				//エラーチェック省略
				if(res){
					var retArray = eval(res);
					this.timeStamp = retArray[0];
					//割り当てられたobjectIDを設定する
					for(var i = 0, k = s.pendingGlobalStageObjectList.length; i < k; i++){
						console.log("Network:Object has allocated ID:" + retArray[1][i]);
						s.pendingGlobalStageObjectList[i].objectID = retArray[1][i];
						s.globalStageObjectList.push(s.pendingGlobalStageObjectList[i]);
					}
				}
				s.pendingGlobalStageObjectData = new RequestData();
				s.pendingGlobalStageObjectList = new Array();
			}
			//ユーザーIDが設定されている=オンライン状態
			//更新データを取得
			//送信データを準備
			var aData = new RequestData();
			if(s){
				s.appendSyncDataTo(aData);
			}
			//送信
			//同期モード
			var res = this.sendRequestSync("POST", URL_PCD_Root + "update.php?uid=" + mainManager.userID + "&action=refresh", aData.data);
			this.syncSub(res);
		}
	},
	syncSub: function(res){
		if(res){
			var s = mainManager.runningStage;
			var retArray = eval(res);
			this.timeStamp = retArray[0];
			//削除処理
			for(var i = 0; i < retArray[3].length; i++){
				console.log("Network:removeObject:ObjectID=" + retArray[3][i] + "\n");
				var anObject = s.getGlobalStageObject(retArray[3][i]);
				if(anObject){
					s.removeStageObject(anObject);
				}
			}
			//更新処理
			for(var i = 0; i < retArray[1].length; i++){
				//mainManager.debugOut("Network:refreshObject:ObjectID=" + retArray[1][i] + "\n");
				var anArray = retArray[1][i];
				var anObject = s.getGlobalStageObject(anArray[0]);
				if(anObject){
					anObject.origin.x = anArray[1];
					anObject.origin.y = anArray[2];
					anObject.movingSpeed.x = anArray[3];
					anObject.movingSpeed.y = anArray[4];
					anObject.attribute = eval(anArray[6]); 
					anObject.updateAttribute();
				}
			}
			//追加処理
			for(var i = 0; i < retArray[2].length; i++){
				var anArray = retArray[2][i];
				var args = eval(anArray[7]);
				var anObject = eval("new " + anArray[5] + "(s, args, true);");
				if(anObject){
					console.log("Network:addObject:ObjectID=" + anArray[0] + "\n");
					anObject.objectID = anArray[0];
					anObject.ownerUID = anArray[8];
					anObject.origin.x = anArray[1];
					anObject.origin.y = anArray[2];
					anObject.movingSpeed.x = anArray[3];
					anObject.movingSpeed.y = anArray[4];
					anObject.attribute = eval(anArray[6]); 
					anObject.updateAttribute();
					s.addStageObject(anObject, true, true);
				}
			}
			//ユーザーリスト
			mainManager.userManager.userList = retArray[4];
		}
	},
	joinStage: function(stage){
		if(mainManager.userID != 0){
			//同期モード
			var res = this.sendRequestSync("POST", URL_PCD_Root + "update.php?uid=" + mainManager.userID + "&action=add", null);
			
			//レスポンス確認
			if(res){
				var retArray = eval(res);
				this.timeStamp = retArray[0];
				for(var i = 0; i < retArray[2].length; i++){
					var anArray = retArray[2][i];
					var args = eval(anArray[7]);
					var anObject = eval("new " + anArray[5] + "(stage, args, true);");
					if(anObject){
						mainManager.debugOut("Network:addObject:ObjectID=" + anArray[0] + "\n");
						anObject.objectID = anArray[0];
						anObject.ownerUID = anArray[8];
						anObject.origin.x = anArray[1];
						anObject.origin.y = anArray[2];
						anObject.movingSpeed.x = anArray[3];
						anObject.movingSpeed.y = anArray[4];
						anObject.attribute = eval(anArray[6]);
						//stage.addStageObject(anObject, true, true);
					}
				}
			}
		}
	},
	syncTimerTickEventListener: function(event){
		mainManager.networkManager.sync();
	},
}

function UIManager(mManager){
	//ユーザー入出力関連
	//キーボード状態を格納するプロパティの設定
	this.keyState = new Object();
	this.keyState.goLeft = false;
	this.keyState.goRight = false;
	this.keyState.goUp = false;
	this.keyState.goDown = false;
	this.keyState.fire = false;
	this.keyState.select = false;
	this.keyState.numbers = [false, false, false, false, false, false, false, false, false, false]; // 0 - 9
	
	//**イベントリスナー設定**
	//keyDown
	window.addEventListener('keydown', this.keyDownEventListener, true);
	//keyUp
	window.addEventListener('keyup', this.keyUpEventListener, true);
}
UIManager.prototype = {
	keyID: {
		//キーコードの設定
		cursorLeft: 37,		//左カーソル
		cursorRight: 39,	//右カーソル
		cursorUp: 38,		//上カーソル
		cursorDown: 40,		//下カーソル
		goLeft: 37,
		goRight: 39,
		jump: 38,
		fire: 32,
		select: 13			//13 : enter
	},
	setKeyFromEvent: function(event, state){
		var keyCode = event.keyCode;
		if(keyCode == mainManager.UIManager.keyID.cursorLeft){
			mainManager.UIManager.keyState.cursorLeft = state
		}
		if(keyCode == mainManager.UIManager.keyID.cursorRight){
			mainManager.UIManager.keyState.cursorRight = state
		}
		if(keyCode == mainManager.UIManager.keyID.cursorUp){
			mainManager.UIManager.keyState.cursorUp = state
		}
		if(keyCode == mainManager.UIManager.keyID.cursorDown){
			mainManager.UIManager.keyState.cursorDown = state
		}
		if(keyCode == mainManager.UIManager.keyID.goLeft){
			mainManager.UIManager.keyState.goLeft = state
		}
		if(keyCode == mainManager.UIManager.keyID.goRight){
			mainManager.UIManager.keyState.goRight = state
		}
		if(keyCode == mainManager.UIManager.keyID.jump){
			mainManager.UIManager.keyState.jump = state
		}
		if(keyCode == mainManager.UIManager.keyID.fire){
			mainManager.UIManager.keyState.fire = state
		}
		if(keyCode == mainManager.UIManager.keyID.select){
			mainManager.UIManager.keyState.select = state;
		}
		if(48 <= keyCode && keyCode <= 57){
			var keyNo = keyCode - 48;	//押されたキーの値
			mainManager.UIManager.keyState.numbers[keyNo] = state;
		}
		
		if(mainManager.runningStage){
			switch(keyCode){
				case 38:
				case 40:
				case 37:
				case 39:
				case 32:
					event.preventDefault();
					break;
			}
		}
	},
	keyDownEventListener:function(event){
		UIManager.prototype.setKeyFromEvent(event, true);
		//実行中のステージに通知
		if(mainManager.runningStage){
			mainManager.runningStage.keyDown(event);
		}
	},
	keyUpEventListener:function(event){
		UIManager.prototype.setKeyFromEvent(event, false);
		//実行中のステージに通知
		if(mainManager.runningStage){
			mainManager.runningStage.keyUp(event);
		}
	},
	clearInput: function(){
		this.keyState.goLeft = false;
		this.keyState.goRight = false;
	},
	
}

function UserManager(mManager){
	this.userList = {};
	//ingredientListはSelectWidgetItemClassの配列
	this.ingredientList = new Array();
	this.maxIngredients = 10;
	//breadListはパンの得点数値の配列
	this.breadList = new Array();
	
	this.characterBaseClass = MainCharacterClass_Man;
}
UserManager.prototype = {
	loginAs: function(userNameStr){
		//呼び出し元stageにauthformがあることを前提とする
		var i = userNameStr.indexOf("@");
		var url = URL_PCD_Auth + "?action=join&name=" + encodeURIComponent(mainManager.runningStage.authform.userName.value);
		if(i != -1){
			//任意ステージの実行
			var userName = userNameStr.substring(0, i);
			var stageName = userNameStr.substring(i + 1);
			url = URL_PCD_Auth + "?action=devjoin&name=" + encodeURIComponent(userName) + "&stage=" + encodeURIComponent(stageName);
		}
		var res = mainManager.networkManager.sendRequestSync("GET", url, null);
		
		if(res){
			var result = eval(res);
			if(result[3] == 0){
				alert("ログインできません。データベース通信エラーです。");
				return;
			} else if(result[3] == 11){
				alert("すでにその名前は使われています。他の名前を試してください。");
				return;
			} else if(result[3] == 10){
				alert("ID:" + result[0] + " でログインしました。");
				mainManager.userID = result[0];
				mainManager.timeStamp = result[1];
				if(result[4]){
					var r = eval(result[4]);
					if(r){
						this.characterBaseClass = r;
					}
				}
				mainManager.loadStageFromNetwork(result[2]);
				return;
			}
		}
	},
	getUserNameByUID: function(uid){
		if(uid in this.userList){
			return this.userList[uid];
		}
		return "Anonymous";
	},
	addIngredientItem: function(item){
		//retv:isAdded
		if(this.ingredientList.length < this.maxIngredients){
			this.ingredientList.push(item);
			return true;
		}
		return false;
	},
	getSumPoint: function(){
		var p = 0;
		for(var i = 0, k = this.breadList.length; i < k; i++){
			p += this.breadList[i];
		}
		for(var i = 0, k = this.ingredientList.length; i < k; i++){
			if(this.ingredientList[i].point < 0){
				p += this.ingredientList[i].point;
			}
		}
		return p;
	},
	deletePlusPointIngredient: function(){
		for(var i = 0; i < this.ingredientList.length; i++){
			if(this.ingredientList[i].point > 0){
				removeAnObjectFromArray(this.ingredientList, this.ingredientList[i]);
				i--;
			}
		}
	}
}

//
//その他のクラス
//

function Point2D(x, y){
	this.x = x;
	this.y = y;
}
Point2D.prototype = {
	
}

function Rectangle(x, y, width, height){
	this.origin = new Point2D(x,y);
	this.size = new Point2D(width,height);
}
Rectangle.prototype = {
	
}

function OffsetBox(top, bottom, left, right){
	this.top = top;
	this.bottom = bottom;
	this.left = left;
	this.right = right;
}
OffsetBox.prototype = {
	
}

function RequestData(){
	//FormDataがIEで動かないので代替
	this.data = "";
	this.length = 0;
}
RequestData.prototype = {
	append: function(name, data){
		this.data += this.getURLEncodedString(name) + "=" + this.getURLEncodedString(data) + "&";
		this.length++;
	},
	getURLEncodedString: function(str){
		return encodeURIComponent(str).replace(/%20/g, '+');
	}
}