/**
 * product_review.js
 *
 * @version:0.7
 * $Date:2009-9-28
 *
 * Release:
 *  0.1
 *   最初のリリース
 *  0.2
 *   クロスドメインでAPIにアクセス可能に(JSONP対応)
 *  0.3
 *   html書き出し部分の最適化
 *   JOSNP通信のみに最適化（Ajax関数を削除）
 *  0.4
 *   JSONP通信（scriptタグ）にキャッシュキラー・固有IDを追加
 *   タイムアウトの設定（読み込みエラーの判別）を追加
 *   エラー時の処理を追加（停止処理）
 *   ローディング・エラー時のUI表示設定
 *  0.5
 *   html書き出し部分の最適化
 *   IE6のCSSバグ（position:absoluteの時、基準親要素にheight指定が無いと、height:100%が無視される）に対応する為、setCssHeightIEを追加
 *  0.6
 *   レビュー表示エリアチェックを追加
 *  0.6.1
 *   開発用APIパスを変更
 *  0.6.2
 *   JSONP通信の引数に"serviceType"追加
 *   JSONP通信の引数"callback"を"jsonpCallback"に変更
 *  0.6.3
 *   開始件数(offset)を内部管理に変更
 *  0.6.4
 *   ページ繰りナビゲーションの戻るボタンのカウントを内部変数に変更
 *   ページ繰りナビゲーションの次へボタンの表示判別を変更
 *  0.7
 *   ガイドボタンUI書き出し機能追加
 */

/*

DOM解析終了後にのhead部分コンテンツテンプレートから（品目コードを持って）呼び出します。
品目コードが無い場合はレビューは表示されません

addEvent(window, 'domReady', function(){
	XreviewContorller.setup(hinCD);
});

 */


/**
 * @namespace 商品詳細ページで使用するレビュー表示機能
 */
var XreviewContorller = {
	
	/**
	 * 設定オブジェクト
	 */
	options:{
		
		/**
		 * レビュー表示エリアのID
		 * @type string
		 */
		reviewElementID : "X_USER_REVIEW",
		
		/**
		 * レビュー検索APIパス
		 * @type string
		 */
		reviewApiPath : "http://www.scroll-shop.com/plaza/review/comment",
		//reviewApiPath : "http://www3.scroll-shop.com/plaza2/review/comment",
		
		/**
		 * 「ユーザーレビューの書き方について」ページのパス
		 * @type string
		 */
		guidePath:"http://www.scroll-shop.com/guide/review/",
		
		/**
		 * 品目コード
		 * @type string
		 */
		hinCD : null,
		
		/**
		 * レビューデータ格納オブジェクト
		 * @type object
		 */
		reviewData:{},
		
		/**
		 * 表示開始レビュー件数
		 * @type number
		 */
		displayOffset : 1,
		
		/**
		 * ページ毎表示数
		 * @type number
		 */
		displayNumber : 5
	},

	/**
	 * 設定オブジェクトを取得
	 *
	 * @private
	 * @param {string} key 設定オブジェクトのキー
	 * @returns {array|string|object} 設定オブジェクトの内容
	 */
	getOption:function(key){
		return this.options[key];
	},
	/**
	 * 設定オブジェクトに登録
	 *
	 * @private
	 * @param {string} key 設定オブジェクトのキー
	 * @param {array|string|object} param 設定オブジェクトの登録内容
	 */
	setOption:function(key, param){
		this.options[key] = param;
	},
	
	/**
	 * レビュー表示フラグ
	 * @type boolean
	 */
	isDisplay : false,
	
	/**
	 * エラーフラグ
	 * @type boolean
	 */
	isError : false,
	
	/**
	 * 通信時間制限タイマーID
	 * @type string
	 */
	loadingID : null,
	
	/**
	 * キャッシュキラー用フラグ
	 * 真で、APIのクエリーにキャッシュキラーを追加する
	 * @type boolean
	 */
	noCache : true,
	
	/**
	 * scriptタグ用オブジェクト
	 * @type object
	 */
	scriptElement:{},
	
	/**
	 * scriptタグ用カウンター
	 * @type number
	 */
	scriptCount : 0,
	
	/**
	 * 通信時間制限（msec）
	 * @type number 
	 */
	timeout :10000, 
	
	/**
	 * 初期化
	 *
	 * @public
	 * @param {string} hinCD 品目コード
	 */
	setup:function(hinCD){
		// 品目コードが無ければ終了
		if(!hinCD) return false;
		
		// 表示エリアIDの要素が無ければ終了
		if(!document.getElementById(this.getOption('reviewElementID'))) return false;
		
		// 品目コードの登録
		this.setOption('hinCD', hinCD);
		
		//データの取得
		this.update();
	},
	
	/**
	 * データの更新
	 *
	 * @public
	 * @param {number} offset number
	 */
	update:function(offset){

		// すでに読み込み中ならば停止
		if(this.loadingID != null) return false;
		
		// offset引数が有れば登録
		if(offset) this.setOption('displayOffset', parseInt(offset));
		
		//通信用データ作成
		var self = this;
		var url = this.getOption('reviewApiPath');
		var json = {
			hnmkCode : this.getOption('hinCD'),
			hnmkSubCode : null,
			start : this.getOption('displayOffset'),
			count : this.getOption('displayNumber'),
			serviceType:"JSONP",
			jsonpCallback:"XreviewJSONPLoadComplate"
		}
		var dataQuery = this.convertQuery(json);
		
		// エリアが表示されていたらローディング表示
		if(this.isDisplay) this.makeLoadingUI();
		
		// タイマー開始
		this.loadingID = setTimeout(function(){self.getJSONPOnError();}, this.timeout);
		
		// JSONP通信開始
		this.getJSONP(url, dataQuery);
	},
	
	/**
	 * json形式のデータをクエリ文字列に変換
	 *
	 * @private
	 * @param {object} json
	 * @return {string} query string
	 */
	convertQuery:function(obj){
		var temp = [];
		for(var key in obj){
			temp.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
		}
		return temp.join('&');
	},
	
	/**
	 * レビューAPIからJSONP形式で読み込み開始
	 *
	 * @private
	 * @param {string} APIURL APIのURL
	 * @param {string} dataQuery 引数
	 */
	getJSONP:function(APIURL, dataQuery){
		// キャッシュキラー追加
		var noCache = (this.noCache) ? '&nocache=' + (new Date()).getTime() : '';
		
		// スクリプトタグ作成
		var script = this.scriptElement = document.createElement('script');  
		script.setAttribute("type", "text/javascript");
		script.setAttribute("charset", "utf-8");
		script.setAttribute("src", APIURL + '?' + dataQuery + noCache);
		script.setAttribute("id", 'jsonpID'+(++this.scriptCount));

		// スクリプトタグ追加
		document.body.appendChild(script);
	},

	/**
	 * レビューAPIからの読み込みエラー
	 * （タイムアウト時）
	 *
	 * @private
	 */
	getJSONPOnError:function(){
		// エラー報告
		this.setError('review data Load time out');
		
		// スクリプトタグの削除
		this.deleteScript(this.scriptElement);
		this.scriptElement = null;
		
		// ローディングIDを空に
		this.loadingID = null;
		
		return false;
	},
	
	/**
	 * scriptタグの消去
	 *
	 * @private
	 * @param {object} scriptElement
	 */
	deleteScript:function(scriptElement){
		if(scriptElement != null) document.body.removeChild(scriptElement);
	},
	
	
	/**
	 * 取得したデータをチェックして表示
	 *
	 * @public
	 * @param {string} data APIからのjson形式データ
	 */
	checkReviewData:function(data){
		
		// スクリプトタグを消去
		this.deleteScript(this.scriptElement);
		this.scriptElement = null;
		
		// エラーがでていたら停止 
		if(this.isError) return false;
		
		// タイマーをクリア
		clearTimeout(this.loadingID);
		this.loadingID = null;
		
		// エリアが表示されていたらUIマスクを消去
		if(this.isDisplay) this.deleteMaskUI();
		
		
		/* ここからエラーチェック */
		
		// 引数がなければエラーを返す
		if(!data){
			this.setError('No Data');
			return false;
		
		// jsonデータ内エラーハンドラをチェック
		// 正常時のステータスコード'000'以外はエラーを返す
		}else if(data['status']['statusCode'] != '000'){
			this.setError(data['status']['statusCode'] + ' ' + data['status']['statusMessage'] );
			return false;
			
		// jsonデータのコメントの長さをチェック
		// コメントが0件ならエラーを返す
		} else if(data['comments'].length <= 0){
			this.setError('No Comment' );
			return false;
			
		} else {
			/* ここから 表示*/
			
			// データを登録
			this.setOption('reviewData', data);
			
			// 数値データの型を整形
			var start = this.getOption('displayOffset');
			var displayNumber =  this.getOption('displayNumber');
			
			var totlaPoint = parseInt(data['totalPoint']);
			var totalCount = parseInt(data['totalCount']);
			var count = parseInt(data['count']);
			
			// 一度だけ書き出し
			if(!this.isDisplay){
				// 表示場所の要素を取得
				var elm = document.getElementById(this.getOption('reviewElementID'));
				// 全体の平均評価値のHTMLを取得
				var avg = this.getAverageHTML(totlaPoint, totalCount);
				//ユーザレビューの書き方についてボタン
				var guide = this.getGuideBtnHTML(this.getOption('guidePath'));
				// 表示エリアを描画
				this.makeReviewArea(elm, avg, guide);
			}
			
			// 毎回書き換え部分のHTMLを取得
			var maskUI = '<div id="X_USER_REVIEW_MASK"></div>';
			var pageInfo = this.getPageInfoHTML(start, count, totalCount);
			var pagenation = this.getPagenationHTML(start, count, totalCount, displayNumber);
			var comments = this.getContentsHTML(data['comments']);
			
			// bodyを書き換え
			this.makeReviewBody(maskUI + pageInfo + comments + pagenation);
			
			
			// 一度だけ書き出し
			if(!this.isDisplay){
				//エリアを表示
				elm.style.display = 'block';
				//フラグをtrue
				this.isDisplay = true;
			}
			
			// IE6以下のCSSバグ用に高さを設定
			this.setCssHeightIE('X_USER_REVIEW_BODY');
		}
	},

	
	/**
	 * レビュー表示エリアの作成
	 *
	 * @private
	 * @param {object} elm innerHTMLで書き出す要素
	 * @param {string} avg 総平均点 
	 * @param {string} guide ガイドページへのリンクボタン
	 */
	makeReviewArea:function(elm, avg, guide){
		elm.innerHTML = [
				'<div id="X_USER_REVIEW_HEADER"><h3>ユーザーレビュー</h3>',
				((avg)?avg:''),
				((guide)?guide:''),
				'</div><div id="X_USER_REVIEW_BODY"></div>'
			].join('');
	},
	
	/**
	 * レビュー本文の作成
	 *
	 * @private
	 * @pram {string} html innerHTMLで書き出す内容
	 */
	makeReviewBody:function(html){
		var elm = document.getElementById('X_USER_REVIEW_BODY');
		elm.innerHTML = '';
		elm.innerHTML = html;
	},
	
	/**
	 * 平均評価のHTMLを取得
	 *
	 * @private
	 * @param {number} totalPoint
	 * @param {number} totalCount
	 * @return {string}
	 */
	getAverageHTML:function(totalPoint, totalCount){

		var avg = (totalPoint/(totalCount * 5));
		var avgPoint = Math.ceil(50 * avg) / 10;
		var avgPercent = Math.ceil(avg*100);
		return [
			'<div id="X_USER_REVIEW_AVERAGE"><div class="X_STAR_RATING"><span class="X_CURRENT_RATING" style="width:', avgPercent ,'%"></span></div>',
			'<p>平均:<span>' , avgPoint , '</span>点</p></div>'
			].join('');
	},

	/**
	 * ガイドページへのリンクボタンのHTMLを取得
	 *
	 * @private
	 * @param {string} guidePath ガイドページのリンクパス
	 * @return {string}
	 */
	getGuideBtnHTML:function(guidePath){
		return (guidePath) ? [
			'<div id="X_USER_REVIEW_GUIDE_BTN">',
			'<a href="', guidePath, '" target="guide">',
			'<img src="/images/product/pd_review_guide.gif" alt="ユーザーレビューの書き方について" />',
			'</a></div>'
		].join('') : '';
	},

	/**
	 * ページ情報を表示
	 *
	 * @private
	 * @param { number} start 開始レビュー件
	 * @param { number} count 表示レビュー件数
	 * @param { number} totalCount 総レビュー件数
	 * @return {string}
	 */
	getPageInfoHTML:function(start, count, totalCount){
		var end = start + count - 1;
		return ['<div id="X_USER_REVIEW_PAGE"><p>最新' , start , '件〜' , end ,'件表示 （全' , totalCount , '件中）</p></div>'].join('');
	},
	
	/**
	 * ページ繰りコントローラーHTMLの取得
	 *
	 * @private
	 * @param {number} start 開始レビュー件
	 * @param {number} count 表示レビュー件数
	 * @param {number} totalCount 総レビュー件数
	 * @param {number} displayNumber デフォルト表示件数
	 * @return {string}
	 */
	getPagenationHTML:function(start, count, totalCount, displayNumber){
		
		var total = totalCount;
		var count = count;
		var prev = start - displayNumber;
		var next = start + count;
		
		// ボタン部分のHTML作成
		var prevBtn = (prev > 0) ? ['<span onclick="XreviewContorller.update(' , prev , ')" title="前の' , displayNumber ,  '件">&lt;</span>'].join('') : null ;
		var nextBtn = (total >= next) ?  ['<span onclick="XreviewContorller.update(' , next , ')" title="次の' , ((total >= (next - 1 + count )) ? count : total - next + 1) , '件">&gt;</span>'].join('') : null;
		
		// 書き出すナビゲーションがなければ空を返す
		return (!prevBtn && !nextBtn) ? '' : ['<div id="X_USER_REVIEW_PAGENATION"><ul id="X_REVIEW_PAGENATION"><li id="X_REVIEW_PAGENATION_PREV">', prevBtn, '</li><li id="X_REVIEW_PAGENATION_NEXT">', nextBtn, '</li></ul></div>'].join('');
	},
	
	/**
	 * 各レビューHTMLの取得
	 *
	 * @private
	 * @param {array} comments レビューコメント
	 * @return {string}
	 */
	getContentsHTML:function(comments){
		var len = comments.length;
		var html = ['<div id="X_USER_REVIEW_COMMENTS"><dl>'];
		for(var i = 0; i < len; i++){
			//var date = comments[i]['postDate'].replace(/-/g,"/") ;
			var date = comments[i]['postDate'];
			var pointNum = parseInt(comments[i]['point']);
			var pointPercent = Math.ceil((pointNum / 5) * 100);
			html.push('<dt>' , 
				'<div class="X_STAR_RATING" title="' , pointNum , '点"><span class="X_CURRENT_RATING" style="width:', pointPercent ,'%"></span></div>', 
				'<p class="X_USER_NICKNAME">' , comments[i]['nickname'] , '</p>',
				'<p class="X_USER_POSTDATE">投稿日時：' , date , '</p>',
				'</dt><dd>', comments[i]['commentText'], '</dd>');
		}
		html.push('</dl></div>');
		return  html.join('');;
	},
	
	/**
	 * ロード中UIの表示
	 */
	makeLoadingUI:function(){
		this.setCSSClass('X_USER_REVIEW_BODY', 'X_NOW_LOADING');
	},
	
	/**
	 * エラーUI(メッセージ)の表示
	 *
	 * @param {string} str エラーメッセージ
	 */
	setError:function(str){
		//alert('[Error] ' + str);
		this.isError = true;
		if(this.isDisplay) this.setCSSClass('X_USER_REVIEW_BODY', 'X_IS_ERROR');
	},
	
	/**
	 * UIマスクの削除
	 */
	deleteMaskUI:function(){
		this.setCSSClass('X_USER_REVIEW_BODY', '');
	},
	
	/**
	 * クラス属性を書き換え
	 *
	 * @param {string} elmID 要素のID
	 * @param {sring} val 値
	 */
	setCSSClass:function(elmID, val){
		var attr = (Xua.msie && Xua.version<8) ? 'className' : 'class';
		var elm = document.getElementById(elmID);
		elm.setAttribute(attr, val);
	},

	/**
	 * IE6以下ならば引数elmIDの要素に高さスタイルを代入する
	 *
	 * @param {string} elmID 要素のID
	 * @param {sring} val 値（指定が無ければ、offsetHeightを代入する）
	 */
	setCssHeightIE:function(elmID , val){
		if (Xua.msie && Xua.version<7){
			var elm = document.getElementById(elmID);
			elm.style.height = 'auto';
			var val = val || parseInt(elm.offsetHeight) + 'px';
			elm.style.height = val;
		}
	}
};

/**
 * レビューのセットアップ
 *
 * htmlヘッダーから、品目コードを取得する為のグローバル関数
 * 取得と同時にレビューをセットアップする
 *
 * @public
 * @param {string} hinCD 品目コード
 */
var setUserReview = function(hinCD){
	XreviewContorller.setup(hinCD);
};

/**
 * JSONP通信時のレビューAPIからのコールバック関数
 *
 * @public
 * @param data {object} json形式のレビューデータ
 */
var XreviewJSONPLoadComplate = function(data){
	XreviewContorller.checkReviewData(data);
};

