/**
 * [LOCATION MAPS ALPHA] Main Module.
 * @author 佐藤亮介.
 * @required prototype.js, script.aculo.us, lightboxJS
 * @copyright © 2007 MapQuest Corporation. All rights reserved.  
 * @comment HTMLを直接記述する場合はクロスブラウザ対応に注意すること。
 */
 
//--------------------------------------
// common functions
//--------------------------------------

/**
 * setTimeout をメソッド化(関数にlaterを追加)。
 * ref.[http://d.hatena.ne.jp/ajiyoshi/20061215/p1]
 * @param {Object} ms 関数実行までの時間(ミリ秒)。
 * @return 中断したり即実行を扱うオブジェクト。
 */
Function.prototype.later = function(ms) {
	var self = this;
	return function() {
		var args = arguments;
		var thisObject = this;
		var res = {
			complete: false,
			cancel: function() { clearTimeout(PID); },
			notify: function() { clearTimeout(PID); later_func(); }
		};
		var later_func = function() {
			self.apply(thisObject, args);
			res.complete = true;
		};
		var PID = setTimeout(later_func, ms);
		return res;
	};
};


//--------------------------------------
// application functions
//--------------------------------------

/**
 * LOCATION MAPS ALPHA アプリの基本機能を制御するクラス。
 * @version 0.5.0
 */
var MqLocationMaps = Class.create();
MqLocationMaps.prototype = {
	version: '0.5.0',
	/**
	 * MqLocationMaps を初期化します。
	 * @param {Element} container Googleマップを設定するHTML要素。
	 * @param {float} initLat 初期表示位置の緯度。
	 * @param {float} initLng 初期表示位置の経度。
	 * @param {int} initZoom 初期表示ズームレベル。
	 */
	initialize: function(container, initLat, initLng, initZoom)
	{
		//--------------------------------------
		// 特殊レイヤの設定
		//--------------------------------------
		// 進捗表示用レイヤの設定。
		this._progressLayer = $('progressLayer');
		this._disableProgress();

		//--------------------------------------
		// 地図の設定
		//--------------------------------------
		GBrowserIsCompatible();
		this._gMap = new GMap2(container);
		this._gMap.addControl(new GLargeMapControl(), new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(5, 30)));
		this._gMap.addControl(new GMapTypeControl(), new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(5, 30)));
		this._gMap.addControl(new GOverviewMapControl());
		this._gMap.addControl(new GScaleControl(100));
		this._gMap.setCenter(new GLatLng(initLat, initLng), initZoom);
		this._gMap.setMapType(G_NORMAL_MAP);

		//--------------------------------------
		// アイコンの設定
		//--------------------------------------
		this._markIcon = new GIcon();
		this._markIcon.image = "./data/map_flag.gif";
		this._markIcon.shadow = "./data/map_flag_shadow.png";
		this._markIcon.iconSize = new GSize(32, 32);
		this._markIcon.shadowSize = new GSize(36, 34);
		this._markIcon.iconAnchor = new GPoint(16, 34);
		this._markIcon.infoWindowAnchor = new GPoint(10, 1);

		//--------------------------------------
		// ロケーションローダーの設定
		//--------------------------------------
		this._locationLoader = null;
		this._lastCenter = null;
		this._lastZoom = null;
		GEvent.addListener(this._gMap, 'dragstart', this._eventDragStart.bindAsEventListener(this));
		GEvent.addListener(this._gMap, 'moveend', this._eventMoveEnd.bindAsEventListener(this));

		//--------------------------------------
		// ロケーション情報の設定
		//--------------------------------------
		this._isDataLock = false;
		this._locationList = new Array();
		this._locationIndex = 0;

		//--------------------------------------
		// ジオコーダ情報の設定
		//--------------------------------------
		this._geocoder = new GClientGeocoder();
		this._searchMarker = null;
	},
	/**
	 * 指定の位置を中心に地図を表示する。。
	 * @param {GLatLng} center 緯度経度。
	 */
	SetCenter: function(center)
	{
		$("state_label").innerHTML = "指定の位置を表示します。";
		this._isDataLock = true;
		this._gMap.panTo(center);
	},
	/**
	 * ロケーションローダーを設定する。
	 * @param {Object} locationLoader ロケーションローダーオブジェクト。
	 */
	SetLocationLoader: function(locationLoader)
	{
		if (this._locationLoader != locationLoader)
		{
			if (this._locationLoader != null )
			{	// 既に表示しているロケーション情報を削除。
				this._locationLoader.stopLoader();
			} else {
				// 既に表示しているロケーション情報を削除。
				this._clearLocationMarker();
			}
			this._locationLoader = locationLoader;
			if (this._locationLoader != null )
			{	// ローダーを初期化して現在の位置でリフレッシュ。
				this._locationLoader.startLoader(this);
				this._lastCenter = this._gMap.getCenter();
				this._lastZoom = this._gMap.getZoom();
				this._locationDataRefresh();
			}
		}
	},
	/**
	 * ロケーション情報を設定する。
	 * @param {Object} locationList JSON形式で定義されたロケーション情報。
	 */
	SetLocationList: function(locationList)
	{
		// 既に表示しているロケーション情報を削除。
		this._clearLocationMarker();
		// メンバにロケーション情報を設定。
		this._locationList = locationList;
		// 設定されたロケーション情報を地図上に表示。
		for (index = 0; index < this._locationList.length; index++)
		{
			this._addLocationMarker(this._locationList[index]);
		}
	},
	/**
	 * ロケーションマーカーを追加する。
	 * @param {Object} locationItem 追加したい１件分のロケーション情報。
	 */
	_addLocationMarker: function(locationItem)
	{
		// 既に登録したマーカーがある場合は重複回避のため削除する。
		this._removeLocationMarker(locationItem);

		// ロケーションマーカーを作成＆登録する。
		var markerIcon = null;
		var html = "";
		if (this._locationLoader == null) {
			markerIcon = this._markIcon;
			html = this._createWindowInfoHtml(locationItem);
		} else {
			markerIcon = this._locationLoader.CreateMarkerIcon();
			html = this._locationLoader.CreateWindowInfoHtml(locationItem);
		}

		var point = new GLatLng(locationItem['lat'], locationItem['lng']);
		var marker = new GMarker(point, {title:locationItem['name'], icon:markerIcon});
		locationItem['marker'] = marker;

		var locationMaps = this;
		GEvent.addListener(marker, "click", function() {
			locationMaps._isDataLock = true;
			marker.openInfoWindowHtml(html);
		});

		this._gMap.addOverlay(marker);
		return marker;
	},
	/**
	 * ロケーションマーカーを削除する。
	 * @param {Object} locationItem 削除したい１件分のロケーション情報。
	 */
	_removeLocationMarker: function(locationItem)
	{
		if (locationItem['marker'] != undefined) {
		    this._gMap.removeOverlay(locationItem['marker']);
		}
	},
	/**
	 * ロケーションマーカーを全削除する。
	 */
	_clearLocationMarker: function()
	{
		this._gMap.closeInfoWindow(); // 情報ウィンドウも閉じる。
		if (this._locationList != null)
		{
			for (index = 0; index < this._locationList.length; index++)
			{
				this._removeLocationMarker(this._locationList[index]);
			}
			this._locationList = null;
		}
	},
	/**
	 * ロケーション情報の吹き出し表示用のHTMLを作成する。
	 * @param {Object} locationItem 削除したい１件分のロケーション情報。
	 */
	_createWindowInfoHtml: function(locationItem)
	{
		var html = "";

		html = "<table id=\"balloon\"><tr>";
		if (locationItem['image']['enable']) {
			html += "<td>";
			html += "<a href=\"http://www.geoclip.jp/detail.php?contents_id=" +  locationItem['uid'] + "\"><img"
				+ " width='" + locationItem['image']['width'] + "'"
				+ " height='" + locationItem['image']['height'] + "'"
				+ " src='" + locationItem['image']['url'] + "'"
				+ " border=\"0\" /></a>";
			html += "</td>";
		}
		html += "<td>"
				+ "<strong>"
				+ "<a href=\"http://www.geoclip.jp/detail.php?contents_id=" +  locationItem['uid'] + "\">" + locationItem['name'] + "</a>"
				+ "</strong>" + "<br/>"
				+ locationItem['option']['group'] + "<br/>"
				+ locationItem['option']['addr'] + "<br/>"
				+ "Tel." + locationItem['option']['tel'];
		html += "</td>";
		html += "</tr></table>";

		return html;
	},
	//--------------------------------------
	// 地図操作に連動したローケーション情報検索機能。
	// ・Googleマップの"MoveEnd"イベントを監視し、1.5秒無操作なら検索を行う。
	// ・吹き出し表示により地図が自動的にスクロールされた場合は、
	// 　データをロック
	//--------------------------------------
	/**
	 * マップをドラッグされたときに発生するイベントを処理するイベントハンドラです。
	 * @param {Object} event
	 */
	_eventDragStart: function()
	{
		if (this._isDataLock == true)
		{
			this._isDataLock = false;
			$("state_label").innerHTML = "データのロックを解除しました。";
		}
	},
	/**
	 * マップが移動し終わったときに発生するイベントを処理するイベントハンドラです。
	 * @param {Object} event
	 */
	_eventMoveEnd: function()
	{
		this._lastCenter = this._gMap.getCenter();
		this._lastZoom = this._gMap.getZoom();
		$("state_lon").innerHTML = this._lastCenter.lng();
		$("state_lat").innerHTML = this._lastCenter.lat();
		$("state_zoom").innerHTML = this._lastZoom;

		// 1.5秒後にローケーション情報のリフレッシュを要求する。
		// データをロックしている場合は何もしない。
		if (this._isDataLock == true)
		{
			$("state_label").innerHTML = "自動スクロールと認識しました。";
			this._isDataLock = false;
			return;
		}
		else
		{
			$("state_label").innerHTML = "リフレッシュを要求しました。";
			this._locationDataRefresh.bind(this).later(1500)();
		}

	},
	_locationDataRefresh: function()
	{
		$("state_label").innerHTML = "";
		// ローダーの設定がない場合は何もしない。
		if (this._locationLoader == null) { return; }
		// データをロックしている場合は何もしない。
		if (this._isDataLock == true) { return; }
		// 地図が操作中(位置､ズームレベルが変化)の場合は何もしない。
		var center = this._gMap.getCenter();
		var zoom = this._gMap.getZoom();
		if( !this._lastCenter.equals(center) || this._lastZoom != zoom ) { return; }

		// ロケーション情報をリクエストする。
		this._locationLoader.Load();
	},
	//--------------------------------------
	// Google ジオコーダを使った位置検索機能。
	// 2006/12/7に日本の住所や駅などに関するジオコーディング機能が加わりました。
	//--------------------------------------
	LocationSearch: function(keyword, funcSearchResult)
	{
		this._funcSearchResult = funcSearchResult;
		this._geocoder.getLatLng(keyword, this._handleLocationSearch.bind(this));
		return true;
	},
	_handleLocationSearch: function(result)
	{
		// 既に検索マーカーを表示していたら削除(消去)。
		if(this._searchMarker != null) {
			this._gMap.removeOverlay(this._searchMarker);
			this._searchMarker = null;
		}
	　　// 取得できた場合はジャンプして検索マーカーを表示。
		if(result) {
		　　this._gMap.panTo(result);
		　　var marker = new GMarker(result);
			GEvent.addListener(marker, "click", function() {
				marker.showMapBlowup(16, G_MAP_TYPE);
			});
			this._searchMarker = marker;
			this._gMap.addOverlay(this._searchMarker);
		}
		this._funcSearchResult(result);
		this._funcSearchResult = null;
	},
	//--------------------------------------
	// 進捗表示機能。
	//--------------------------------------
	/**
	 * 進捗表示を有効にする(表示する)。
	 */
	_enableProgress: function()
	{
		Element.show(this._progressLayer);
	},
	/**
	 * 進捗表示を無効にする(隠す)。
	 */
	_disableProgress: function()
	{
		Element.hide(this._progressLayer);
	}
}

/**
 * 防災マップ避難所情報の検索をサポートする
 * ロケーションローダークラス。
 * @version 0.5.0
 */
var MqLocationLoader = Class.create();
MqLocationLoader.prototype = {
	version: '0.5.0',
	DataPath: "./data/",
	/**
	 * MqLocationLoader を初期化します。
	 * @param {string} contentsID コンテンツ表示領域のID。
	 */
	initialize: function(titleID, contentsID)
	{
		this.titleID = titleID;
		this.contentsID = contentsID;
		this._locationList = new Array();
	},
	/**
	 * ローダーの機能を開始します。
	 * @param {object} locationMaps "LOCATION MAPS ALPHA"オブジェクト。
	 */
	startLoader: function(locationMaps)
	{
		this._locationMaps = locationMaps;
		this._gMap = locationMaps._gMap;
		$(this.titleID).innerHTML = "防災マップ避難所一覧";
	},
	/**
	 * ローダーの機能を終了します。
	 */
	stopLoader: function()
	{
		$(this.titleID).innerHTML = "";
		$(this.contentsID).innerHTML = "";
		this._locationMaps._clearLocationMarker();
	},
	/**
	 * ローダーの機能を初期化します。
	 * @param {object} locationMaps "LOCATION MAPS ALPHA"オブジェクト。
	 */
	Load: function()
	{
		var zoom = this._gMap.getZoom();
		if (zoom < 14)
		{
			var html = "現在の縮尺では施設検索できません。<br/>拡大してください。<br/>";
			html += "<a id=\"autoZoomAtSearchLevel\" href=\"#\">現在位置で検索できる縮尺に設定</a>";
			$(this.contentsID).innerHTML = html;
			var gMap = this._gMap;
			Event.observe($('autoZoomAtSearchLevel'), 'click', function(){ gMap.setZoom(14); });
		} else {
			// 検索結果の JSON をオブジェクトに格納する。
			var mapBounds = this._gMap.getBounds();
			var pointNE = mapBounds.getNorthEast();
			var pointSW = mapBounds.getSouthWest();
			var url = './searchshelter.ashx';
			//var url = 'http://ryosuke-xp/demo/gpsmaps/searchshelter.ashx';
			var params = 'minX=' + pointSW.lng() + '&minY=' + pointSW.lat() + '&maxX=' + pointNE.lng() + '&maxY=' + pointNE.lat();
			var options = {
					method: 'get', 
					parameters: params, 
					onLoading: this._handleLoading.bindAsEventListener(this),
					onFailure: this._handleFailure.bindAsEventListener(this),
					onSuccess: this._handleSuccess.bindAsEventListener(this)
			};
			new Ajax.Request(url, options);
		}
	},
	/**
	 * 通信開始時に発生するイベントを処理するイベントハンドラです。
	 * @param {object} httpObject HTTP通信オブジェクト。
	 */
	_handleLoading: function(httpObject)
	{
		this._httpObject = httpObject;
		this._locationMaps._enableProgress();
	},
	/**
	 * 通信エラー時に発生するイベントを処理するイベントハンドラです。
	 * @param {object} httpObject HTTP通信オブジェクト。
	 */
	_handleFailure: function(httpObject)
	{
		this._locationMaps._disableProgress();
		// TODO: ここでエラーログを出力させたい。
	},
	/**
	 * 通信成功時に発生するイベントを処理するイベントハンドラです。
	 * @param {object} httpObject HTTP通信オブジェクト。
	 */
	_handleSuccess: function(httpObject)
	{
		this._httpObject = null;
		try
		{
			eval('var locationList = ' + httpObject.responseText);
			this._locationList = locationList;
			this.UpdateLocationList(locationList);
			this._locationMaps.SetLocationList(locationList);
			this._locationMaps._disableProgress();
		}
		catch(error)
		{
			// TODO: ここでエラーログを出力させたい。
		}
	},
	/**
	 * マーカー表示に使用されるイメージを作成します。
	 * @return {object} GIcon GIconオブジェクト。
	 */
	CreateMarkerIcon: function()
	{
		var markIcon = new GIcon();
		markIcon.image = this.DataPath + "shelter1.gif";
		markIcon.shadow = this.DataPath + "shelter1_shadow.png";
		markIcon.iconSize = new GSize(32, 32);
		markIcon.shadowSize = new GSize(36, 34);
		markIcon.iconAnchor = new GPoint(15, 28);
		markIcon.infoWindowAnchor = new GPoint(14, 1);
		return markIcon;
	},
	/**
	 * 情報ウィンドウ表示用のHTMLを作成します。
	 * @param {Object} locationItem １件分のロケーション情報。
	 * @return {string} 情報ウィンドウ表示用のHTML。
	 */
	CreateWindowInfoHtml: function(locationItem)
	{
		var html = "";

		html = "<table id=\"balloon\"><tr>";
		if (locationItem['image']['enable']) {
			html += "<td>";
			html += "<a href=\"" + this.DataPath + "photo/" + locationItem['image']['url'] + "\" target=\"_blank\" rel=\"lightbox\"><img"
				+ " width=\"" + locationItem['image']['width'] + "\""
				+ " height=\"" + locationItem['image']['height'] + "\""
				+ " src=\"" + this.DataPath + "photo/" + locationItem['image']['url'] + "\""
				+ " border=\"0\" /></a>";
			html += "</td>";
		}
		html += "<td>"
				+ "<strong>"
				+ "<a href=\"#\">" + locationItem['name'] + "</a>"
				+ "</strong>" + "<br/>"
				+ locationItem['option']['group'] + "校区<br/>"
				+ locationItem['option']['addr'] + "<br/>"
				+ "Tel." + locationItem['option']['tel'];
		html += "</td>";
		html += "</tr></table>";

		return html;
	},
	/**
	 * 情報ウィンドウ表示用のHTMLを作成します。
	 * @param {Object} locationItem １件分のロケーション情報。
	 * @return {string} 情報ウィンドウ表示用のHTML。
	 */
	UpdateLocationList: function(locationList)
	{
		var html = "";
		var locationItem = null;
		if (locationList.length == 0)
		{
			html = "現在の表示範囲に避難所は見つかりません。";
			$(this.contentsID).innerHTML = html;
		}
		else
		{
			$(this.titleID).innerHTML = "防災マップ避難所一覧 (" + locationList.length + "件)";
			var linkID = "";
			html = "<ul>";
			for (idx = 0; idx < locationList.length; idx++)
			{
				locationItem = locationList[idx];
				linkID = "locationLink" + locationItem['uid'];
				html += "<li>";
				if (locationItem['image']['enable']) {
					html += "<a href=\"" + this.DataPath + "photo/" + locationItem['image']['url'] + "\" target=\"_blank\" rel=\"lightbox\"><img"
						+ " width=\"" + locationItem['image']['width'] + "\""
						+ " height=\"" + locationItem['image']['height'] + "\""
						+ " src=\"" + this.DataPath + "photo/" + locationItem['image']['url'] + "\""
						+ " align=\"left\""
						+ " border=\"0\" />" + "</a>";
				}
				html += "<a id=\""+ linkID + "\" href=\"#\">" + locationItem['name'] + "</a><br/>";
				html += locationItem['option']['group'] + "校区<br/>";
				html += locationItem['option']['addr'] + "<br/>";
				html += "Tel." + locationItem['option']['tel'] + "<br clear=\"all\"/>";
				html += "</a>";
				html += "</li>";
			}
			html += "</ul>";

			$(this.contentsID).innerHTML = html;

//			var locationMaps = this._locationMaps;
//			for (idx = 0; idx < locationList.length; idx++)
//			{
//				locationItem = locationList[idx];
//				var center = new GLatLng(locationItem['lat'], locationItem['lng']);
//				linkID = "locationLink" + locationItem['uid'];
//				Event.observe($(linkID), 'click', function(){ locationMaps.SetCenter(center); });
//			}

		}
	}
}

