dojo.require("dojo.parser");
dojo.require("dijit.form.Button");
dojo.require("dijit.Menu");
dojo.require("dijit._Templated");
dojo.require("dijit._Container");
dojo.require("dojo.i18n");
dojo.require("dojo.string");
dojo.require("ibm.portal.search.widgets.common");
dojo.require("com.ibm.portal.xslt");
dojo.provide("ibm.portal.search.widgets.ScopeSearchWidget");

dojo.declare(
	"dijit.SearchDropDownButton", dijit.form.DropDownButton,
	{
	//summary:
	//	a specific type of drop-down button for search
	
	//menuIcon: String
	//	the url of an icon to display for drop-down
	menuIcon: "",
	
	tooltip: "",
	
	//override template
	templateString:"<div class=\"dijit dijitLeft dijitInline\"\r\n\tdojoAttachEvent=\"onmouseenter:_onMouse,onmouseleave:_onMouse,onmousedown:_onMouse,onclick:_onDropDownClick,onkeydown:_onDropDownKeydown,onblur:_onDropDownBlur,onkeypress:_onKey\"\r\n\t><div class='dijitRight'>\r\n\t<button class=\"dijitStretch dijitButtonNode dijitButtonContents\" type=\"${type}\"\r\n\t\tdojoAttachPoint=\"focusNode,titleNode\" waiRole=\"button\" waiState=\"haspopup-true,labelledby-${id}_label\"\r\n\t\t><span class=\"dijitButtonText\" \tdojoAttachPoint=\"containerNode,popupStateNode\"\r\n\t\tid=\"${id}_label\">${label}</span\r\n\t\t>\r\n\t</button>\r\n</div></div>\r\n"
});


dojo.declare(
	"dijit.ScopeMenuItem", dijit.MenuItem,
	{
	//summary:
	//	a specific type of dijit.MenuItem for search scopes: based on scope object
		
	//override dijit.MenuItem's template
	templateString1: '<tr class="dijitMenuItem" dojoAttachEvent="onmouseover:_onHover,onmouseout:_onUnhover,ondijitclick:_onClick"><td><div class="dijitMenuItemIcon">',

	templateString2: '<img width="16" height="16" border="0" src="${iconSrc}" title="${altText}"  alt="${altText}"/>',
	
	templateString3: '</div></td><td tabIndex="-1" class="dijitMenuItemLabel" dojoAttachPoint="containerNode" waiRole="menuitem"></td><td dojoAttachPoint="arrowCell"><div class="dijitMenuExpand" dojoAttachPoint="expand" style="display:none"><span class="dijit_a11y dijitInline dijitArrowNode dijitMenuExpandInner">+</span></div></td></tr>',

	// item: ibm.portal.search.scope.Scope
	//	The scope object
	item: null,
	
	//override dijit._Widget's method
	postMixInProperties: function(){
		this.item.label = this.item.label.replace(/&amp;/g, "&");
		this.label = this.item.label;
		this.iconSrc = this.item.icon;
		this.altText =  (this.item.description == null)? this.label: this.item.description;
		this.templateString = this.templateString1 + 
			((this.item.icon.length == 0)? "": this.templateString2) + 
			this.templateString3;
	},
	
	setLabel: function(/*String*/ value){
		this.label=value;
		var newNode = document.createTextNode(value);
		this.containerNode.innerHTML= "";
		this.containerNode.appendChild(newNode);
	},
	
	//override dijit.MenuItem's method
	onClick: function() {
		dojo.publish(ibm.portal.search.scope.SCOPE_SELECTED_TOPIC, [this.item])
	}
});

dojo.declare(
	"dijit.SearchLinkMenuItem", dijit.ScopeMenuItem,
	{
	//summary:
	//	a specific type of dijit.MenuItem for search links
	
	//newWindow: Boolean
	//	determine whether to open the link in a new window
	newWindow: false,
	
	//override dijit._Widget's method
	postMixInProperties: function(){
		this.item.label = this.item.label.replace(/&amp;/g, "&");
		this.label = this.item.label;
		this.iconSrc = this.item.icon;
		this.altText =  this.label;
		this.templateString = this.templateString1 + 
			((this.item.icon == null || this.item.icon.length == 0)? "": this.templateString2) + 
			this.templateString3;
		this.newWindow = this.item.newWindow;
	},
	
	//override dijit.MenuItem's method
	onClick: function() {
		if (this.newWindow) {
			ibm.portal.search.util.openSideWindow(this.item.link);
		} else {
			window.location = this.item.link;
		}
	}
});

dojo.declare(
	"dijit.CustomLinkMenuItem", dijit.SearchLinkMenuItem,
	{
	//summary:
	//	a specific type of dijit.MenuItem for search custom links
	
	scopeSearchWidget: null,
	
	//override dijit.MenuItem's method
	onClick: function() {
		var queryText = this.scopeSearchWidget.getQueryText();
		queryText = queryText.replace(/"/g, "\\\"");
		var newWindow = window.open();
		//The delayed focus was added to solve a problem in IE
		//The location is set only after the focus to avoid cross-frame-scripting issues
		newWindow.setTimeout("focus(); document.location = \"" + this.item.link + queryText + "\";", 100);
	}
});

dojo.declare(
   "ibm.portal.search.widgets.ScopeSearchWidget",
   [dijit._Widget, dijit._Templated, dijit._Container],
   {
   	//summary:
	//	a widget which allows the user to choose a scope and enter query, in order to perform search.
	
	//define template by external file
	templatePath: dojo.moduleUrl('ibm.portal.search.templates','searchBox.html'),
	
	//widgetsInTemplate: Boolean
	//	override dijit._Templated's default value
	widgetsInTemplate: true,
	
	//submitUrl: String
	//	the URL to whom the search form will be submitted
	submitUrl: '',

	//searchIcon: String
	//	the URL of the icon which is used for submitting the search form
	searchIcon: null,
	
	//searchMenuIcon: String
	//	the url of an icon to display for drop-down
	searchMenuIcon: null,
	
	//sourceContentNode: String
	//	contains the id of the current content node
	sourceContentNode: '',
	
	//language: String
	//	the language of the query
	language: null,
	
	//xsltUrl: String
	//	the URL of the XSLT which trasforms scopes XML to JSON format
	xsltUrl: dojo.moduleUrl('ibm.portal.search.xslt','scopes.xsl'),
	
	//enabledTextClass: String
	//	The CSS class of the query text
	enabledTextClass: 'wpsPortletToolbarText',
	
	//disabledTextClass: String
	//	The CSS class of an empty query box, which displays the name of the search scope
	disabledTextClass: 'wpsPortletToolbarDisabledText',
	
	//contextualScopes: Array
	//	an array of contextual scopes, added by the user
	contextualScopes: null,
	
	//searchLinks: Array
	//	an array of additional search links for the search menu, added by the user
	searchLinks: null,
	
	//menuTTL: number
	//	The time (in milliseconds) to cache the search menu in the browser
	menuTTL: 1000 * 60 * 15,//15 minutes

	//resourceBundle: Object
	//	A map between keys and translated strings
	resourceBundle: null,
	
	scopes: null,
	
	emptyScope: {id: "", label: "", description: "", icon: ""},
	
	searchFeedUrl: null,
	
	timeStamp: null,
	
	//override dijit._Widget's method
	startup: function () {
		dojo.connect(this.scopeButton, '_openDropDown', this, 'createSearchMenu');
		this.scopeButton.titleNode.title = this.resourceBundle.scopeButtonTooltip;
		dojo.addClass(this.scopeButton.domNode, 'searchForm');
		dojo.addClass(this.scopeMenu.domNode, 'searchForm');
		dojo.removeClass(this.scopeMenu.domNode, 'dijitMenu');
		dojo.removeClass(this.scopeMenu.domNode, 'dijitMenuTable');
		this.scopeMenu.domNode.cellSpacing = 0;
		dojo.subscribe(ibm.portal.search.scope.SCOPE_SELECTED_TOPIC, this, 'scopeSelected');
		dojo.subscribe(ibm.portal.search.NEW_QUERY_TOPIC, this, 'newQuery');
		dojo.subscribe(ibm.portal.search.scope.ADD_SCOPE_TOPIC, this, 'addContextualScope');
		dojo.subscribe(ibm.portal.search.scope.SUBMIT_SCOPE_TOPIC, this, 'submitScope');
	},
	
	//override dijit._Widget's method
	postMixInProperties: function(){
		this.scopeService = ibm.portal.search.util.getServiceUrl(this.searchFeedUrl, 'scopes', {
			locale: djConfig.locale,
			customLinks: true,
			timeStamp: this.timeStamp
		});
		this.retrieveScopes();
		
		var scopeItem = this.scopes[0];
		this.selectedScope = new ibm.portal.search.scope.Scope(scopeItem.label, 
					scopeItem.description, scopeItem.icon, scopeItem.id);
	},
	
	//summary:
	//	this function will be called following a 'SCOPE_SELECTED' event
	//newScope: ibm.portal.search.scope.Scope
	//	the scope object that was selected by the user
	scopeSelected: function(/*ibm.portal.search.scope.Scope*/ newScope) {
		if (djConfig.isDebug) {
			console.debug("scopeSelected");
		}
		this.selectedScope = newScope;
		if (dojo.hasClass(this.query, this.disabledTextClass)) {
			this.query.value = newScope.label;
		}
	},
	
	//summary:
	//	add a contextual scope to the search menu
	//scope: ibm.portal.search.scope.Scope
	//	the scope object
	//isDefault: Boolean
	//	Optional (default is false). Add this scope as the currently selected scope.
	addContextualScope: function(/*ibm.portal.search.scope.Scope*/ scope, /*Boolean?*/ isDefault) {
		if (djConfig.isDebug) {
			console.debug("addContextualScope");
		}
		if (this.contextualScopes == null) {
			this.contextualScopes = [];
		}
		if (isDefault) {
			this.scopeSelected(scope);
		} 
		this.contextualScopes.push(scope);
	},
	
	//summary:
	//	add a link the search menu
	addSearchLink: function(/*Object*/ linkObj) {
		if (djConfig.isDebug) {
			console.debug("addSearchLink");
		}
		if (this.searchLinks == null) {
			this.searchLinks = [];
		}
		this.searchLinks.push(linkObj);
	},
	
	//summary:
	//	this function will be called following a 'NEW_QUERY' event
	//newQuery: ibm.portal.search.Query
	//	the query that was submitted
	newQuery: function(/*ibm.portal.search.Query*/ newQuery) {
		if (djConfig.isDebug) {
			console.debug("ScopeSearchWidget: newQuery");
		}
		this.query.value = newQuery.text;
		if (dojo.hasClass(this.query, this.disabledTextClass)) {
			dojo.removeClass(this.query, this.disabledTextClass);
			dojo.addClass(this.query, this.enabledTextClass);
		}
		this.onBlur();
	},
	//summary:
	//	this function retrieves the scopes from the server, or from browser cache
	retrieveScopes: function() {
		dojo.xhrGet({
			url: this.scopeService,
			load: dojo.hitch(this, "handleScopes"),
			error: dojo.hitch(this, "handleScopeError"),
			sync: true,
			handleAs: 'text'
		});
	},
	
	//summary:
	//	this function handles the scope information retrieved from the server
	handleScopes: function(response, ioArgs) {
		var escapedResponse = this.escapeQuotes(response);
	
		var scopesXml = ibm.portal.xml.xslt.loadXmlString(escapedResponse);//required because of IE issues
		var fullXsltUrl = window.location.protocol + "//" + window.location.host + this.xsltUrl;
		var xslTransformer = com.ibm.portal.xslt.TRANSFORMER_FACTORY.newTransformer(fullXsltUrl);
		var resultString = xslTransformer.transformToDocument(scopesXml, null, true);
		this.scopes = dojo.fromJson(resultString);
		if (this.scopes.length == 0) {
			this.scopes.push(this.emptyScope);
		}
		if (this.scopes[this.scopes.length - 1] == null) {
			//handle IE problem with redundant comma
			this.scopes.pop();
		}
	},
	//escape any quotes inside the body of an xml tag
	escapeQuotes: function(/*String*/ text) {
		var buffer = [];
		//keep track of when you are inside a tag using a tag counter
		var tagCounter = 0;
		for (var i = 0; i < text.length;  i++) {
			var c = text.charAt(i);
			if (c == '<') {
				tagCounter++;
			} else if (c == '>') {
				tagCounter--;
			}
			if (c == '"' && tagCounter == 0) {
				buffer.push("\\\"");
			} else {
				buffer.push(c);
			}
		}
		return buffer.join("");
	},
	
	//summary:
	//	this function handles an error when retrieving scopes from the server
	handleScopeError: function(response, ioArgs) {
		this.scopes = [this.emptyScope];
	},
	
	//summary:
	//	this function is called when the user clicks the drop-down button to create the menu
	createSearchMenu: function(){
		if (djConfig.isDebug) {
			console.debug("createSearchMenu");
		}
		if (this.menuBuildTime != null) {
			var currentTime = new Date().getTime();
			if (currentTime - this.menuBuildTime > this.menuTTL) {//if time-to-leave has passed
				if (this.scopeMenu != null && this.scopeMenu.hasChildren()) {
					var menuItems = this.scopeMenu.getChildren();
					//removing existing menu items
					for (var i = 0; i < menuItems.length; i++) {
						this.scopeMenu.removeChild(menuItems[i]);
					}
				}
			} else {//other wise, leave menu as is
				if (djConfig.isDebug) {
					console.debug("menu already exist");
				}
				return;
			}			
		}
		this.menuBuildTime = new Date().getTime();
		
		this.addContextualScopesMenuItems();
		this.addScopeMenuItems();
		this.addSearchLinksMenuItems();
	},
	//summary:
	//	this menu items for contextual scopes
	addContextualScopesMenuItems: function() {
		if (this.contextualScopes == null) {
			return;//no need for special search links
		}
		for (var i = 0; i < this.contextualScopes.length; i++) {
			this.scopeMenu.addChild(new dijit.ScopeMenuItem({item: this.contextualScopes[i]}));
		}
		this.scopeMenu.addChild(new dijit.MenuSeparator({}));
	},
	//summary:
	//	this function adds additional search links
	addSearchLinksMenuItems: function() {
		if (this.searchLinks == null) {
			return;//no need for special search links
		}
		this.scopeMenu.addChild(new dijit.MenuSeparator({}));
		for (var i = 0; i < this.searchLinks.length; i++) {
			this.scopeMenu.addChild(new dijit.SearchLinkMenuItem({item: this.searchLinks[i]}));
		}
	},
	
	//summary:
	//	this function adds the scope items to the menu
	addScopeMenuItems: function() {
		if (djConfig.isDebug) {
			console.debug("addScopeMenuItems");
		}
		var scopeItems = this.scopes;
		
		var firstCustomLink = true;
		for (var i = 0; i < scopeItems.length; i++) {
			var scopeItem = scopeItems[i];
			if (scopeItem.link == null) {//this is a regular scope
				var scopeObj = new ibm.portal.search.scope.Scope(scopeItem.label, 
					scopeItem.description, scopeItem.icon, scopeItem.id);
				this.scopeMenu.addChild(new dijit.ScopeMenuItem({item: scopeObj}));
			} else {//this is a custom link
				if (firstCustomLink) {
					this.scopeMenu.addChild(new dijit.MenuSeparator({}));
					firstCustomLink = false;
				}
				this.scopeMenu.addChild(new dijit.CustomLinkMenuItem({item: scopeItem, scopeSearchWidget: this}));
			}
		}
	},
	
	//summary:
	//	get the text from the query box.
	getQueryText: function () {
		var inputBoxes = dojo.query("input", this.domNode);
		var textBuffer = [];
		for (var i = 0; i < inputBoxes.length; i++) {
			var inputBox = inputBoxes[i];
			var inputBoxText = dojo.string.trim(inputBox.value);
			if (inputBox.type == "hidden" || 
				inputBoxText.length == 0 || 
				dojo.hasClass(inputBox, this.disabledTextClass)) {
				continue;
			}
			textBuffer.push(inputBoxText);
		}		
		return textBuffer.join(" ");
	},
	
	//summary:
	//	this function is called when the search form is submitted
	submitQueryForm: function(event) {
		if (djConfig.isDebug) {
			console.debug("submitQueryForm");
		}
		var queryText = this.getQueryText();
		if (queryText.length == 0) {
			alert(this.resourceBundle.emptyBoxMessage);
			dojo.stopEvent(event);
			return false;
		}
		
		if (queryText.length > 100
			|| this.selectedScope.isContextual()) { 
			//maximal URL length in IE is 2048 (and more in other browsers), but let's stay on the safe side
			this.queryForm.method.value = 'POST';
		}
		
		if (dojo.hasClass(this.query, this.disabledTextClass)) {
			this.query.value = "";
		}
		
		this.scope.value = encodeURIComponent(dojo.toJson(this.selectedScope));
		return true;
	},
	//summary:
	//	update scope and query parameters and submits the search form
	submitScope: function(/*ibm.portal.search.scope.Scope*/ scope, /*String?*/ query) {
		if (djConfig.isDebug) {
			console.debug("submitScope");
		}
		
		if (scope != ibm.portal.search.scope.SELECTED_SCOPE) {
			this.queryForm.method.value = 'POST';
			this.scope.value = encodeURIComponent(dojo.toJson(scope));
		} else {
			this.scope.value = encodeURIComponent(dojo.toJson(this.selectedScope));
		}
		
		this.query.value = (query != null)? query: '';
		if (scope.query != null) {
			this.query.value = this.query.value + " " + scope.query;
		}
		
		this.queryForm.submit();
	},
	//summary:
	//	this function is called when the user focuses on the search input field
	onTextFocus: function() {
		if (dojo.hasClass(this.query, this.disabledTextClass)) {
			this.query.value = "";
			dojo.removeClass(this.query, this.disabledTextClass);
			dojo.addClass(this.query, this.enabledTextClass);
		}
	},
	//summary:
	//	this function is called when the user removed focus from the search input field
	onTextBlur: function() {
		if (this.query.value == "") {
			dojo.removeClass(this.query, this.enabledTextClass);
			dojo.addClass(this.query, this.disabledTextClass);
			this.query.value = this.selectedScope.label;			
		}
	}
});