/**
 * hotel_results_oMap.js
 * Advanced application sample that displays search results in
 * the list and Google maps<br/>
 * 検索結果をリストと Google Maps に表示するサンプル
 */

// for browser that dosn't support console function
// consoleをサポートしないブラウザ用
if (typeof window.console != "object") {
	var console = {};
	console.log   = function(arg){};
	console.error = function(arg){};
}

/**
 * We use the namespace to avoid conflicts. VCSDB stands for ValueCommerce Service Data Base.<br/>
 * 名前の衝突を避けるために namespace を使用します。VCSDB は ValueCommerce Service DataBase を意味します。
 * @namespace VCSDB
 * @ignore
 */
var VCSDB = {};

/**
 * Users can choose location from dropdown menu<br/>
 * ユーザはドロップダウンメニューで場所を指定することができます。
 * @class LocationMenu
 * @requires JQuery 1.4
 * @see <a href="http://jquery.com/">http://jquery.com/</a>
 */
VCSDB.LocationMenu = function(){
    var _sAreaDataFile = "locationCodes.json";
    
	/**
	 * This initializes select box to choose location area<br/>
	 * エリア選択の内容を初期化。
	 * @method _init
	 * @private
	 * @param  Json {object} Location data in Json format <br/> JSON フォーマットのエリアデータ
	 * @param  Status {string} Loading status <br/> 読み込みステータス
	 * @return {void}
	 */
	var _init = function(oJson, sStatus){
		console.log(oJson);
			
		_updateAreaChoice("larea_cd", oJson["code"]);
		
		// adding event listener to onChange event on large area choice
		// 大エリア選択の onChange イベントリスナーを追加
		$("#larea_cd").change(function(oArg){
			console.log("large area changed.");
			console.log("option #%s selected", this.selectedIndex);
			
			$("#sarea_cd").empty();
			$("#city_cd").empty();
            $("#VCSDB-search-button").attr("disabled", true);
            
			var nIndex = this.selectedIndex,
				aArea  = nIndex > 0 ? oJson["code"][nIndex-1]["sArea"] : null;
			
            if (aArea) {
                _updateAreaChoice("sarea_cd", aArea);
            }
		});
		
		// adding event listener to onChange event on prefecture (small area) choice
		// 小エリア選択にイベントリスナーを追加
		$("#sarea_cd").change(function(oArg){
			console.log("small area changed.");
			console.log("option #%s selected", this.selectedIndex);
			
			$("#city_cd").empty();
            $("#VCSDB-search-button").attr("disabled", true);
            
			var nIndex       = this.selectedIndex,
				nParentIndex = $("#larea_cd")[0].selectedIndex,
				aArea        = nIndex > 0 && nParentIndex > 0 ? oJson["code"][nParentIndex-1]["sArea"][nIndex-1]["city"] : null;
			
            if (aArea) {
    			_updateAreaChoice("city_cd", aArea);
            }
		});

		// adding event listener to onChange event on city choice
		// 都市選択にイベントリスナーを追加
		$("#city_cd").change(function(oArg){
			console.log("city changed.");
			console.log("option #%s selected", this.selectedIndex);
			
			if (this.selectedIndex > 0) {
                $("#VCSDB-search-button").attr("disabled", false);
            } else {
                $("#VCSDB-search-button").attr("disabled", true);
            }
		});
	};
    
	/**
	 * This updates area choise data in select box<br/>
	 * エリア選択の内容を更新します。
	 * @method _updateAreaChoice
	 * @private
	 * @param  Id {string} Id of target select element <br/> 対象となる select 要素の Id
	 * @param  AreaData {array} Data of child areas <br/> 子要素のエリアデータ
	 * @return {void}
	 */
	var _updateAreaChoice = function(sId, aAreaData){
		console.log("updating area choice");
		console.log(aAreaData);
		var oFragment = document.createDocumentFragment(),
            oOption1  = document.createElement("option"),
            sText1    = document.createTextNode("選択してください");
        
        oOption1.appendChild(sText1);
        oFragment.appendChild(oOption1);
        
		$.each(aAreaData, function(nIndex, oArea){
			var oOption = document.createElement("option"),
				sText   = document.createTextNode(oArea["name"]);
			
			oOption.value = oArea["cd"];
			oOption.appendChild(sText);
			oFragment.appendChild(oOption);
		});
		$("#"+sId).append(oFragment);
	};
    
	// create location choices and add event listener to the search form when document ready
	// ページ読み込み時にエリア選択を初期化
	$(document).ready(function(){
		console.log("initializing location menu...");
		$.getJSON(_sAreaDataFile, _init);
	});    
}();


/**
 * This displays the hotel data in Google Maps<br />
 * ホテル情報を Google Maps 上に表示します。
 * @class MapView
 * @requires jQuery 1.4, Google Maps API v3
 * @see <a href="http://jquery.com/">http://jquery.com/</a>
 * @see <a href="http://code.google.com/apis/maps/documentation/v3/">http://code.google.com/apis/maps/documentation/v3/</a>
 */
VCSDB.MapView = function(){
	var GEvent        = google.maps.event,
        GLatLng       = google.maps.LatLng,
        GLatLngBounds = google.maps.LatLngBounds;
        
    var _oCurrentInfoWindow = null,
        _oMarkers = {},
        _oMap     = {},
        _oMapConf = {
            mapcenter: {lat:33.67806845, lng:135.34804344},
            zoomlevel: 14
        }; // default values

	/**
	 * This creates markers on Map for the given item <br/>
	 * 受け取ったアイテムに対してマーカーを作成
	 * @method _createMarker
	 * @private
	 * @param  {object}  Item An item of search results <br/> 検索結果のアイテム
	 * @return {object}  Marker (Marker object of Google Map)
	 */
	var _createMarker = function(oItem){
		console.log("creating a marker for "+ oItem["hotel"]["id"]);
		// LatLng oItem["hotel"]["lat"], oItem["hotel"]["lng"]
		// Price  oItem["hotel"]["priceMin"], oItem["hotel"]["priceMax"]
		// Title  oItem["title"]
		// ID     oItem["hotel"]["id"]
		// URL    oItem["link"]   (clickthrough link)
		
		var nLat    = oItem["hotel"]["lat"],
			nLng    = oItem["hotel"]["lng"];
		
		if (!nLat || !nLng) {
			return false;
		}
		
		// creating contens for info window for the marker
		// Marker の info window を作成
		var oHotelInfo = document.createElement("div"),
			oA         = document.createElement("a"),
			oText      = document.createTextNode(oItem["title"]);
		
		oHotelInfo.id        = "marker_" + oItem["hotel"]["id"];
		oHotelInfo.className = "hotel-info";
		oA.href              = oItem["link"];
		oA.target            = "_blank";
				
		oA.appendChild(oText);
		oHotelInfo.appendChild(oA);
		
		var oLatLng     = new google.maps.LatLng(nLat, nLng),
			oMarker     = new google.maps.Marker({
				position: oLatLng,
				title: oItem["title"]
			}),
			oInfoWindow = new google.maps.InfoWindow({
				content: oHotelInfo
			});
		
        _oMarkers[oItem["hotel"]["id"]] = { "marker": oMarker, "infowindow": oInfoWindow};
        
        // Add event listener for clicking marker to open info window
        // and blink clicked hotel in the list
        // マーカーがクリックされた時に、吹き出しを開く + リスト内の
        // 対象となるホテルが点滅するよう、イベントリスナーを追加
		GEvent.addListener(oMarker, "click", function(){
			VCSDB.MapView.openInfo(oItem["hotel"]["id"]);

            var sLiId  = "hotel_" + oItem["hotel"]["id"];
            
            // Adjust the scroll position of search result list
            // 検索結果リストのスクロール位置を調整
            var oContainer = $("#VCSDB-list"),
                oCntOffset = oContainer.offset(),
                oCntHeight = oContainer.height();
            
            var oListItem  = $("#"+sLiId),
                oLiOffset  = oListItem.offset(),
                oLiHeight  = oListItem.height();
            
            var curSrcTop  = oContainer.scrollTop();
            
            if (oCntOffset.top > oLiOffset.top) {
            	oContainer.scrollTop( curSrcTop + oLiOffset.top - oCntOffset.top );
            }
            if (oCntHeight + oCntOffset.top < oLiOffset.top + oLiHeight) {
            	var delta     = oLiOffset.top - (oCntHeight + oCntOffset.top),
            	    newScrTop = curSrcTop + oLiHeight + delta;
            	
            	oContainer.scrollTop( newScrTop );
            }
            
            $("#" + sLiId).fadeTo("fast", 0.3).fadeTo("fast", 1);
		});
		
		return oMarker;
	};
    
	/**
	 * This initializes Google Map <br/>
	 * Google Map を初期化
	 * @method  _initMap
	 * @private
	 * @return  {void}
	 */
	var _initMap = function(){
		console.log("initializing google map...");
		var oLatLng  = new google.maps.LatLng(_oMapConf.mapcenter.lat, _oMapConf.mapcenter.lng),
			oOptions = {
				zoom: _oMapConf.zoomlevel,
				center: oLatLng,
				mapTypeId: google.maps.MapTypeId.ROADMAP
			};
		
		_oMarkers = {};
		_oMap     = new google.maps.Map(document.getElementById("VCSDB-map"), oOptions);
		
		// this just checks current center and output to console when map is dragged
		// 以下は地図をドラッグした際、console に中央の座標を出力する
		// GEvent.addListener(this.map, "dragend", function(oEvent){
		//	console.log(this.getCenter());
		// });
	};
  
	// execute _initMap() when the page is loaded
	// ページが読み込まれたときに、_initMap() を実行
	$(document).ready(_initMap);

	return {
        /**
         * This opens infor window on the specified hotel <br />
         * 指定されたホテルのマーカーに吹き出しを表示
         * @method openInfo
         * @param Id {string} Hotel ID
         * @return {void}
         */
        openInfo: function(sId){
            console.log("opening info window for the hotel: " + sId);
            if (_oCurrentInfoWindow){
                _oCurrentInfoWindow.close();
            }
            
            _oMarkers[sId]["infowindow"].open(_oMap, _oMarkers[sId]["marker"]);
            _oCurrentInfoWindow = _oMarkers[sId]["infowindow"];
        },
        
		/**
		 * This displays search SDB results in #VCSDB-results <br/>
		 * 検索結果を #VCSDB-results に表示
		 * @method show
		 * @param  {object} SearchResults Search results in JSON format <br/> JSON フォーマットの検索結果
		 * @return {void}
		 */
		show: function(oSearchResults){				
			console.log("show method called.");
			
			var sStatus      = "",
				nResultCount = 0,
                aLatitudes   = [],
                aLongitudes  = [];
            
			try {
				sStatus      = oSearchResults["status"];
				nResultCount = oSearchResults["resultCount"];
			} catch(e) {
				console.error("can not get status/resultcount correctly: " + e);
			}
			
			var aItem = sStatus === "OK" && nResultCount > 0 ? oSearchResults["items"] : [];
			
			console.log("%s items returned.", aItem.length);
			console.log(aItem);
			
			if (aItem.length < 1) {
				$("#VCSDB-status").empty().append("<p>検索結果がありません</p>");
				return;
			}
			
			// creating list from items in the search results
            // 検索結果リストの項目を作成
            _initMap();
			$("#VCSDB-status").empty().append("<p>マーカーを作成中...</p>");
			for (var i=0, l=aItem.length; i<l; i++){
				var oMarker = _createMarker(aItem[i]);
				oMarker.setMap(_oMap);
                
                aLatitudes.push(parseFloat(aItem[i]["hotel"]["lat"]));
                aLongitudes.push(parseFloat(aItem[i]["hotel"]["lng"]));
			}
            
            //getting min/max lat/lng and display map according to the point
            // lat/lng の最大・最小値を取得し、そのポイントに合う地図を表示
            aLatitudes.sort();
            aLongitudes.sort();
            
            var minLat = aLatitudes.shift(),
                maxLat = aLatitudes.pop() || minLat,
                minLng = aLongitudes.shift(),
                maxLng = aLongitudes.pop() || minLng;
            
            var minLatLng = new GLatLng(minLat, minLng),
                maxLatLng = new GLatLng(maxLat, maxLng),
                bounds    = new GLatLngBounds(minLatLng, maxLatLng);
            
            _oMap.fitBounds(bounds);
            
			$("#VCSDB-status").empty();
		}
	};
}();

/**
 * Manages search results<br />
 * 検索結果を処理
 * @class SearchResults
 * @requires JQuery 1.4
 * @see <a href="http://jquery.com/">http://jquery.com/</a>
 */
VCSDB.SearchResults = function(){
    var _sDefaultImg = "images/noimage.png",
        _resultcache = {}; // page data cache. 各ページのデータ
    
    var _sLiTemplate = "<a href='{link}' target='_blank'>"
                     + "<img src='{imgsrc}' class='VCSDB-img'></a>"
                     + "<div class='VCSDB-description'><a href='{link}' target='_blank'>{title}"
                     + "</a><br><div class='VCSDB-price'> {pricemin}円 ～ {pricemax}円</div>"
                     + "{pvimg}<br>"
                     + "<div class='VCSDB-book_btn'><a href='{link}' target='_blank'><img src='images/vc_btn_booknow.gif'></a></div>"
                     + "</div>"
                     + "<div class='c-both'></div>"
                     + "";
    
	/**
	 * This creates list element from given item<br/>
	 * 指定されたアイテムに対してリストエレメントを作成
	 * @method _createListItem
	 * @private
	 * @param  Item {object}  An item object of search results <br/> 検索結果のアイテムオブジェクト
	 * @return ListItem {string}  HTML string
	 */
	var _createListItem = function(oItem){
		console.log("creating a list element for "+ oItem["hotel"]["id"]);
		var oLi = document.createElement("li");
        
        oLi.id        = "hotel_" + oItem["hotel"]["id"];
        oLi.className = "VCSDB-item";
        
		var sImgSrc = _sDefaultImg;
		try {
			var sImgFree = oItem["hotel"]["images"][0]["imageSet"][0]["free"]["url"];
			sImgSrc = sImgFree || sImgSrc;
		} catch (e) {
			console.log("no image found for " + oItem["hotel"]["id"]);
		}
		
        var oData = {
            "title": oItem["title"],
            "link": oItem["link"],
            "imgsrc": sImgSrc,
            "pricemin": oItem["hotel"]["priceMin"],
            "pricemax": oItem["hotel"]["priceMax"],
            "pvimg": oItem["hotel"]["pvImg"]
        };
        
		var sLi = _sLiTemplate.replace(/{([^{}]*)}/g,
            function (a, b) {
                var r = oData[b];
                return typeof r === 'string' || typeof r === "number" ? r : "";
            }
        );

        $(oLi).append(sLi);
        
		return oLi;
	}
    
    /**
     * renders search result list with pager<br />
     * 検索結果のリストをページング付きで表示
     * @method _renderList
     * @private
     * @param Item {array}
     * @param ResultCount {number}
     * @param ResultPerPage {number}
     * @param Page {number}
     * @return {void}
     */
    var _renderList = function(aItem, nResultCount, nResultPerPage, nPage){
        console.log("%s items to be displayed", aItem.length);
        console.log(aItem);
        
        if (aItem.length < 1) {
            $("#VCSDB-list").empty().append("<p>検索結果がありません</p>");
            return;
        }
            
        // creating list from items in the search results
        // 検索結果一覧を作成
        $("#VCSDB-list").empty().append("<p>検索結果のリストを作成中...</p>");
        var oUl = document.createElement("ul");
        for (var i=0, l=aItem.length; i<l; i++){
            var sLi = _createListItem(aItem[i]);
            $(oUl).append(sLi);
        }
        $("#VCSDB-list").empty().append(oUl);
        
        // Add event listener for click the list item
        // to open info window on the map
        // クリック時に、地図上に吹き出しを表示
        $(oUl).children("li").click(function(oEvent){
            var sId = this.id.replace(/hotel_/, "");
            VCSDB.MapView.openInfo(sId);
        });
        
        // generate pager with JQuery Pager
        // JQuery Pager でページング機能を作成
        nResultCount = nResultCount < 1000 ? nResultCount : 1000; // SDB returns max 1000 items
        var nPageCount = Math.ceil(nResultCount/nResultPerPage);
        console.log("pageCount: " + nPageCount);
        $("#VCSDB-pager").pager({
            pagenumber: nPage,
            pagecount: nPageCount,
            buttonClickCallback: VCSDB.HotelSearch.get
        });
    };
    
    return {
        /**
         * clear cache data<br />
         * ページのキャッシュデータをクリア
         * @method clearResultCache
         * @return {void}
         */
        clearResultCache: function(){
            _resultcache = {};
        },
        /**
         * returns page data from cache<br />
         * 指定されたページのデータをキャッシュから取得
         * @method getPageCache
         * @param Page {number}
         * @return Data {object}
         */
        getPageCache: function(nPage){
            return _resultcache["p" + nPage];
        },
		/**
		 * This displays search results in #VCSDB-results<br/>
		 * 検索結果を #VCSDB-results に表示。
		 * @method show
		 * @param  {object} SearchResults Search result data in JSON format<br/>JSON フォーマットの検索結果
		 * @return {void}
		 */
		show: function(oSearchResults){
			console.log("show method called.");
			var sStatus         = "",
				nResultCount    = 0,
				nPage           = 1,
                nResultPerPage  = 10;
				
			try {
				sStatus         = oSearchResults["status"];
				nResultCount    = oSearchResults["resultCount"];
				nPage           = oSearchResults["page"];
                nResultPerPage  = oSearchResults["resultPerPage"];
			} catch(e) {
				console.error("can not get status/resultcount correctly: " + e);
			}
			
			var aItem = sStatus === "OK" && nResultCount > 0 ? oSearchResults["items"] : [];
			
			console.log("%s itesm found", nResultCount);
			_renderList(aItem, nResultCount, nResultPerPage, nPage);
            VCSDB.MapView.show(oSearchResults);
            
			// update search results cache to avoid sending request for the same query
			// 同じ検索条件で検索した時にサーバにリクエストしないよう、データを保持する
			_resultcache["p" + nPage] = oSearchResults;
        }
    };
}();

/**
 * Manges hotel search request<br />
 * ホテル検索処理
 * @class HotelSearch
 * @requires JQuery 1.4
 * @see <a href="http://jquery.com/">http://jquery.com/</a>
 */
VCSDB.HotelSearch = function(){
    
	var _config = {
		url:              "http://ws.valuecommerce.ne.jp/travel/search?",
		token:            "1-2315bb1403d5a5a6fa761388058acdc3",
		serv_type:        "1",
		ctryname:         "",
		format:           "jsonp",
		results_per_page: "10"
	};
    
	$(document).ready(function(){
		console.log("initializing search function...");
		$("#VCSDB-search").submit(function(oEvent){
			// prevent refresh the page by submitting
			// submit 時にページがリフレッシュするのを防ぐ
			oEvent.preventDefault();
			// refresh cache when submit new query
			// 新しく検索が実行されたら、保持しているページのデータをクリアする
            VCSDB.SearchResults.clearResultCache();
			// then send request to SDB
            VCSDB.HotelSearch.get();
		});
	});
    
    return {
        /**
         * This sends request to Travel Service API <br/>
         * ValueCommerce Service API にリクエストします。
         * @method get
         * @param  Page {number} Requested page number <br/> 検索結果ページの番号
         * @return {void}
         */
        get: function(nPage){
            console.log("get method called.");
            
            nPage = nPage ? nPage : 1;
            
            var oCache = VCSDB.SearchResults.getPageCache(nPage);
            if (oCache) {
                console.log("cached data found");
                VCSDB.SearchResults.show(oCache);
                return;
            }
            
            var sRequestUrl = _config.url + "callback=?",
                oParams = {
                            "token":      _config.token,
                            "serv_type":  _config.serv_type,
                            "ctry_name":  _config.ctryname,
                            "larea_cd":   $("#larea_cd").val() || "",
                            "sarea_cd":   $("#sarea_cd").val() || "",
                            "city_cd":    $("#city_cd").val() || "",
                            "format":     _config.format,
                            "page":       nPage,
                            "results_per_page": _config.results_per_page
                        };
            
            console.log("sending request with parameter:");
            console.log(oParams);
            
            $.getJSON(sRequestUrl, oParams, VCSDB.SearchResults.show);
            $("#VCSDB-list").empty().append("<p>検索中...</p>");
        }
    }
}();

