
/**
 * apply経由でスコープを保持して関数を呼び出す
 */
function  b(obj, func){
    return function(){
//		return func.apply(obj, arguments);
		try {
			return func.apply(obj, arguments);
		}
		catch(e){
			//log(obj.name + "::" +e);
			return null;
		}
    };
}


/**
 * clone object
 * @param {Object} obj
 */
function clone(obj){
	var f = function(){	};
	f.prototype = obj;
	return new f();
}


var RerankJp = {
	
	//logを出力するか
	debug : false,
	
	//tagcloudを再構築するか
	reconstructCloud : true,
	//const
	/**
	 * 1回の検索結果取得件数
	 */
	requestUnit: 50,
	/**
	 * 検索結果取得の間隔
	 */
	searchInterval : 200,
	
	/**
	 * ボタンが消えるまでの時間
	 */
	floatTimeout : 5000,	
	
	enableUndo : true,
	
	/**
	 * "元に戻す"操作の最大記録
	 */
	maxUndo : 3,
	
	//format
	header : "<div class=\"left\" id=\"header_left\"><span id=\"operation\"><strong>Web Search</strong></span></div><div class=\"info\" id=\"info\"></div><div class=\"right\"> Results <strong>$start$</strong> - <strong>$numbers$</strong> of about <strong>$totalResultsAvaiable$</strong> for <strong>$query$</strong></div>",
	searching: "Searching <strong>$query$</strong> ...",
	info : "<span id=\"operation\"><strong>$operation$</strong> <strong>$term$</strong></span> <span id=\"undo\" class=\"enable\">Undo</span>",
	notfound : '<span class="404">Sorry, No Results for <strong>$query$</strong></span>',
	
	
	query : null,
	queryList : [],
	queryRegList : [],
	num : 200,
	items: [],
	completeFlags : [],
	lastBlock : -1,
	floatTimer : null,
	highlights : [],
	maxTotalResult : 0,
	floatOffsetY : 15,
	floatOffsetX : -15,
	prevOperations: [],	//"元に戻す"
	
	
	
	/**
	 * 初期化
	 */
	initialize : function(){
		//formイベントのバインド
		$E.initialize();

		if(!this.checkQuery()){	//クエリを受け取ってない場合は何もしない
			return false;
		}
		
		//検索結果取得の開始
		this.startSearch();
		
	},
	
	clear : function(){
		$('#main_result').get(0).innerHTML = '<ol id="result"></ol><div id="loading"></div>';
		$('#result_header').get(0).innerHTML = "";
	},
	
	checkQuery : function(){
		if(this.getParamFromURL('p',null)){
			var params = this.getSearchParams();
			if(params.query == "Please input query!"){
				return false;
			}
			$("#textQuery").val($.trim(params.query));
			$("#num").val(this.getParamFromURL('num',"200"));
			return true;
		}
		else{	
			return false;
		}
	},

	/**
	 * ここを起点に
	 */
	startSearch : function(){
		var params = this.getSearchParams();
		this.params = params;
		this.query = this.params.query;
		var queryList = this.query.replace(/"/g,"").split(/[\s　]/g);
		//var queryList = this.query.split(/[\s　]/g);
		var list = [];
		for(var i = 0, n = queryList.length; i < n; i++){
			q = queryList[i].replace(/["']/g,"");
			q = queryList[i];
			if(q.length > 1){
				list.push(q);
			}
		}
		this.queryList = list;
		this.queryRegList = [];
		for(i = 0, n = this.queryList.length; i < n; i++){
			var reg = new RegExp(this.escapeRegExp(this.queryList[i]),"ig");
			this.queryRegList.push(reg);
		}
		this.num = this.params.num;
		
		
		//unit回ずつRequestを送って結果を表示する
		var block = Math.floor( (this.num - 1) / this.requestUnit) + 1;
		//全部falseで初期化
		var completeFlags = new Array(block);
		completeFlags = $.map(completeFlags,function(){
			return false;
		});
		this.completeFlags = completeFlags;
		
		//ここから実際に検索開始
		this.clear();
		$('#result_header').get(0).innerHTML = this.searching.replace('$query$',this.escapeHTML(this.query));
		this.startIndicator();
		//logging
		$L.search(this.query,this.num);
		for(i = 0; i < block; i++){
			var search = new YahooSearch();
			var p = {
				'query' : this.query,
				'num' : this.requestUnit,
				'start' : i * this.requestUnit + 1,
				'block' : i
			};
			search.doSearch(p,this.searchInterval * i,b(this,this.recieveResultBlock));
		}
		
	},
	

	/**
	 * 今のところqueryとnumだけ
	 */
	getSearchParams : function(){
		var query = $.trim(decodeURIComponent(this.getParamFromURL('p',"").replace(/[+]/g,' ')));
		query = query.replace(/[\s 　]/g," ");
		query = query.replace(/[ ]+/," ");
		var num = this.getParamFromURL('num',200);
		var params = {
			'query': query,
			'num' : Number(num)
		};
		return params;
	},
	
	/**
	 * URLからパラメータを取得
	 */
	getParamFromURL : function(key,defaultValue){
		var urlArray = document.location.href.split("?");
		if(urlArray.length && urlArray.length > 1){
			var params = urlArray[1].split("&");
			for(var i = 0, n = params.length; i < n; i++){
				var kv = params[i].split('=');
				if(kv[0] == key){
					return kv[1];
				}
			}
		}
		return defaultValue;
	},
	
	
	/**
	 * 検索結果を取得し終えたrequestごとに呼ばれる
	 * @param {Array} items
	 * @param {String} query
	 * @param {Number} totalResults
	 * @param {Number} totalReturnedItems
	 * @param {Number} block
	 */
	recieveResultBlock : function(items,query,totalResults,totalReturnedItems,block){
		var maxResult = Math.max(this.maxTotalResult,totalResults);
		this.maxTotalResult = maxResult;

	
		var source = this.makeHtml(items);
		
		if (block >= 1 && items && items.length >= 1 && items.length < 50) {	//端数
			if(this.prevNum){
				
			}
			else{
				this.items = this.items.concat(items);
				$('#result').get(0).appendChild(source);
				this.prevNum = items.length;
			}
		}
		else {
			if (this.lastBlock < block) {
				this.items = this.items.concat(items);
				$('#result').get(0).appendChild(source);
			}
			else { //結果の順番が前後する場合
				var beforeindex = block * this.requestUnit;
				var id = "#item" + beforeindex;
				if ($(id)) { //真ん中に挿入
					$(id).after(source);
					this.items = items.concat(this.items); //本当は実際の表示と並び方が違うが暫定的に
				}
				else { //先頭に挿入
					$('#result').append(source);
					this.items = items.concat(this.items);
				}
			}
		}
		this.lastBlock = block;
		this.completeFlags[block] = true;
		var resultNum = 0;
		if(this.items){
			resultNum = this.items.length;
		}
		else{
			resultNum = 0;
		}
		var header = this.makeSearchHeader(query,maxResult,resultNum);
		$('#result_header').get(0).innerHTML = header;	//html()は遅いので使用しない		
		if(this.checkComplete()){	//検索結果を全て取得し終わったら
			this.stopIndicator();
			$("#result_header").addClass('finish');
			if(!this.items || this.items.length < 1){	//結果が0件
				$("#result_header").get(0).innerHTML = "<div class=\"left\" id=\"header_left\"><strong>ウェブ検索結果</strong></div>";
				$("#result").get(0).innerHTML = this.notfound.replace("$query$",this.escapeHTML(this.query));
				return;
			}
			$E.bindItems();
			$T.createTagCloud();
		} 
		else{
			
		}
	},

	makeSearchHeader : function(query,totalResult,num){
		return $R.header.replace("$start$",1).replace("$numbers$",num).replace("$totalResultsAvaiable$",this.number_format(totalResult)).replace("$query$",query);
	},
	
	/**
	 * 全てのリクエストが終了したかを確認する
	 */
	checkComplete: function(){
		for(var i = 0, n = this.completeFlags.length; i < n ; i++){
			if(!this.completeFlags[i]){
				return false;
			}
		}
		return true;
	},
		
	/**
	 * 検索結果要素をresultに挿入する
	 * @param {Array} items
	 */
	makeHtml : function(items){
		//innerHTML ? documentFragment ?
		var df = document.createDocumentFragment();
		for(var i = 0, n = items.length; i < n; i++){
			df.appendChild(items[i].toHtml());
		}
		return df;
	},
	
	/**
	 * termを選択した際に，選択した語を含む検索結果の背景色を変更する
	 * @param {String} term
	 * @param {String} type
	 * @param {Number} currentRank
	 * @param {Object} element
	 */
	highlightItems : function(term,type,currentRank,element){
		//IEはli要素のbackgroundColorの変更がやけに重いので除外
		if($.browser.msie){
			return;
		}
		var targets = []; //highlightするitem集合
		if (!element) {
			return;
		}
		var from = Math.max(0,currentRank - 5);
		var to_num =  Math.min(this.items.length,currentRank + 5);
		
		for(var i = from; i <= to_num; i++){
			var item = this.items[i];
			var elm = item.elm;
			if(item.isContain(term,type)){
				$(elm).css("backgroundColor","#ddffdd");
				targets.push(elm);
			}
		}
		this.highlights = targets; 
	},
	
	clearHighlight : function(){
		for(var i = 0, n = this.highlights.length; i < n; i++){
			$(this.highlights[i]).css("backgroundColor","#ffffff");
		}
		this.highlights = [];
	},
	
	/** rerank **/
	
	prepareRerank : function(){
		this.hideFloat();
		if(this.rerankTimer){
			clearTimeout(this.rerankTimer);
			this.rerankTimer = null;
		}
		var sel = this.escapeRegExp($E.selectString);
		var termReg; var replaceReg;
		try {
			termReg = new RegExp();
			replaceReg = new RegExp();
			termReg.compile(sel.replace(" ","[ ]?"),"i");
			replaceReg.compile('(<span class="(em|del)">)*' + sel.replace(" ","[ ]?") + '(</span>)*',"i");
		}
		catch(e){
			log(e);
			return false;
		}
		this.selectString = $E.selectString;
		this.termReg = termReg;
		this.replaceReg = replaceReg;	
		
		if (this.enableUndo) {
			if (this.prevOperations.length >= this.maxUndo) {
				this.prevOperations.shift();
			}
			if (this.prevOperations.length < this.maxUndo) {
				var operation = [];
				operation.push($('#result').get(0).innerHTML);
				var master = this.items;
				var slave = clone(master);
				this.items = slave;
				operation.push(master);
				operation.push($('#operation').get(0).innerHTML);
				this.prevOperations.push(operation);
			}
		}
		
		//tagcloudを消去
		if (this.reconstructCloud) {
			$T.initialize();
		}
		return true;	
	},
	
	undo : function(){
		if(this.prevOperations && this.prevOperations.length >= 1){
			var operation = this.prevOperations.pop();
			$('#result').get(0).innerHTML = operation[0];
			this.items = operation[1];
			var elms = $("#result").get(0).childNodes;
			for(var i = 0, n = this.items.length; i < n; i++){
				this.items[i].makeElementFromSource(elms[i]);
			}
			$('#operation').get(0).innerHTML = operation[2];	
		}
		if (this.prevOperations.length == 0) {
			$('#undo').get(0).setAttribute('class', 'disable');
		}
	},
	
	termEmphasis : function(){
		//logging
		$L.rerank("emphasis",$E.lastActiveType,$E.selectString,$E.lastBaseNode);		
		var resultNum  = this.items.length;
		var df = document.createDocumentFragment();
		var upList = [];
		var downList = [];
		log('TermEmphasis::' + this.selectString + "++" + $E.lastActiveType + ';;' + resultNum);
		var selectString = this.selectString;
		var type = $E.lastActiveType;
		var termReg = this.termReg;
		var replaceReg = this.replaceReg;
		for(var i = 0; i < resultNum; i++){
			var item = this.items[i];
			var elm = item.elm;
			if(item.isContain(termReg,type)){
				item.termEmphasis(this.escapeHTML(selectString),termReg,replaceReg);
				df.appendChild(item.elm);
				upList.push(item);
			}
			else{
				downList.push(item);
			}
		}
		$("#result").prepend(df);
		this.items = upList.concat(downList);
		try {
			document.getElementById('result_header').scrollIntoView();
			//document.getElementById('formarea').scrollIntoView();
		}catch(e){
			
		}	
		

		this.lastOperation = "<span class=\"emphasis\">Emphasize</span>";

		this.afterRerank();
		
	 
	},
	
	termDeletion : function(){
		//logging
		$L.rerank("deletion",$E.lastActiveType,$E.selectString,$E.lastBaseNode);	
		var upList = [];
		var downList = [];
		var df = document.createDocumentFragment();
		var selectString = this.selectString;
		var type = $E.lastActiveType;
		var termReg = this.termReg;
		var replaceReg = this.replaceReg;
		for(var i = 0, n = this.items.length; i < n; i++){
			var item = this.items[i];
			var elm = item.elm;
			if(item.isContain(termReg,type)){
				item.termDeletion(this.escapeHTML(selectString),termReg,replaceReg);
				df.appendChild(item.elm);
				downList.push(item);
			}
			else{
				upList.push(item);
			}
		}
		$("#result").get(0).appendChild(df);
		
		this.items = upList.concat(downList);

		this.lastOperation = "<span class=\"deletion\">Delete</span>";		
		this.afterRerank();

	},
	
	afterRerank : function(){
		var term = $E.selectString;
		if(term.length > 12){
			term = term.substring(0,10) + "...";
		}
		$('#header_left').get(0).innerHTML = this.info.replace('$term$',this.escapeHTML(term)).replace('$operation$',this.lastOperation);
		if(!this.enableUndo){
			$('#undo').get(0).innerHTML = "";
		}
		else {
				$('#undo').bind('click', b($R, $R.undo));

		}
		//tagcloudを再構築するかどうか
		if (this.reconstructCloud) {
			$T.createTagCloud();		
		}
	},
	
	/** indicator **/
	
	 startIndicator : function(){
		$("#loading").get(0).innerHTML = '<div id="indicator"><img src="' + $C.indicator + '" /></div>';
	},
	
	stopIndicator : function(){
		$("#loading").get(0).innerHTML = '';
	},
	
	/* float button */
	startFloat : function(x,y){
		var emCss = {
			"position" : "absolute",
			"top" : (y - 40) + "px",
			"left" : (x - 20) + "px",
		}
		var delCss = {
			"position" : "absolute",
			"top" : (y + 10) + "px",
			"left" : (x - 21) + "px",
		}
		$("#buttonEmphasis").css(emCss);
		$("#buttonDeletion").css(delCss);
		$("#buttonEmphasis").show();
		$("#buttonDeletion").show();
		this.floatTimer = setTimeout(b(this,this.hideFloat),this.floatTimeout);
	},
	
	hideFloat : function(){
		
		if(this.floatTimer){
			clearTimeout(this.floatTimer);
			this.floatTimer = null;
		}
		
		if(this.activeElement){
			$(this.activeElement).removeClass('active');
			this.activeElement = null;
		}
		
		this.clearHighlight();
		$("#buttonEmphasis").hide();
		$("#buttonDeletion").hide();
	},

/*	
	startFloat : function(x,y){
		var cssObj =  {
			"position" : "absolute",
			"top" : y + this.floatOffsetY,
			"left" : x + this.floatOffsetX,
			"visibility" : "visible",
			"display" : "block"
		};
		$('#floatbutton').css(cssObj);
		$('#floatbutton').show();
		this.floatTimer = setTimeout(b(this,this.hideFloat),this.floatTimeout);
	},
	
	hideFloat : function(){
		
		if(this.floatTimer){
			clearTimeout(this.floatTimer);
			this.floatTimer = null;
		}
		
		if(this.activeElement){
			$(this.activeElement).removeClass('active');
			this.activeElement = null;
		}
		
		this.clearHighlight();
		$("#floatbutton").hide();
	},
*/


	number_format : function(val){
		return val.toString().replace( /([0-9]+?)(?=(?:[0-9]{3})+$)/g , '$1,' );
	},
	
	
	/*util*/
	
	
	a_reg : /&/g,
	a_str : "&amp;",
	b_reg : /"/g,
	b_str : "&quot;",
	c_reg : />/g,
	c_str : "&gt;",
	d_reg : /</g,
	d_str : "&lt;",
	escapeHTML : function(str){
		return str.replace(this.a_reg,this.a_str).replace(this.b_reg,this.b_str).replace(this.c_reg,this.c_str).replace(this.d_reg,this.d_str);
	},
	
	eh : this.escapeHTML,
	
	e_a : /\./ig,
	s_a : "\\.",
	e_b : /\)/ig,
	s_b : "\\)",
	e_c : /\(/ig,
	s_c : "\\(",
	e_d : /\?/ig,
	s_d : "\\?",
	e_e : /\+/ig,
//	s_e : "\\+",
	e_f : /\{\}/ig,
	s_f : "\\{",
	e_g : /\}/ig,
	s_g : "\\}",
	e_h : /\*/ig,
	s_h : "\\*",		
	escapeRegExp : function(str){
		return str.replace(this.e_a,this.s_a).replace(this.e_b,this.s_b).replace(this.e_c,this.s_c).replace(this.e_d,this.s_d).
					replace(this.e_e,this.s_e).replace(this.e_f,this.s_f).replace(this.e_g,this.s_g).replace(this.e_h,this.s_h);
	}
};

function log(str){
	if ($R && $R.debug) {
		$("#log").get(0).innerHTML += str + "<br>\r\n";
	}
}

$R = RerankJp;

$('document').ready( function(){
	$R.initialize();
});
