dojo.provide("epages.cartridges.de_epages.content.widget.TagEditor");
dojo.require("dijit.form.ComboBox");
dojo.require("epages.widget.FormElement");
dojo.require("dojo.data.util.simpleFetch");
dojo.require("dojo.data.util.filter");
dojo.require("dojo.regexp");

parent.epages.cartridges.de_epages.content.widget.valueGrouped = {}; // collection of all selected tags in TagEditors grouped by a groupname

dojo.declare(
	"epages.cartridges.de_epages.content.widget._TagEditorMenu",
	dijit.form._ComboBoxMenu,
	{
		templateString: "<ul class='dijitReset dijitMenu dijitTagEditor' style='background-color:#FFF;' dojoAttachEvent='onmousedown:_onMouseDown,onmouseup:_onMouseUp,onmouseover:_onMouseOver,onmouseout:_onMouseOut' tabIndex='-1' style='overflow: \"auto\"; overflow-x: \"hidden\";'>"
			+"<li class='dijitMenuPreviousButton' dojoAttachPoint='previousButton' waiRole='option'></li>"
			+"<li class='dijitMenuNextButton' dojoAttachPoint='nextButton' waiRole='option'></li>"
		+"</ul>",
		itemClasses : "dijitReset",

		createOptions: function(results, dataObject, labelFunc){
			// summary:
			//		Fills in the items in the drop down list
			// results:
			//		Array of dojo.data items
			// dataObject:
			//		dojo.data store
			// labelFunc:
			//		Function to produce a label in the drop down list from a dojo.data item

			//this._dataObject=dataObject;
			//this._dataObject.onComplete=dojo.hitch(comboBox, comboBox._openResultList);
			// display "Previous . . ." button
			this.previousButton.style.display = (dataObject.start == 0) ? "none" : "";
			dojo.attr(this.previousButton, "id", this.id + "_prev");
			// create options using _createOption function defined by parent
			// ComboBox (or FilteringSelect) class
			// #2309:
			//		iterate over cache nondestructively
			dojo.forEach(results, function(item, i){
				var menuitem = this._createOption(item, labelFunc);
				menuitem.className = this.itemClasses;
				dojo.attr(menuitem, "id", this.id + i);
				this.domNode.insertBefore(menuitem, this.nextButton);
			}, this);
			// display "Next . . ." button
			var displayMore = false;
			//Try to determine if we should show 'more'...
			if(dataObject._maxOptions && dataObject._maxOptions != -1){
				if((dataObject.start + dataObject.count) < dataObject._maxOptions){
					displayMore = true;
				}else if((dataObject.start + dataObject.count) > (dataObject._maxOptions - 1)){
					//Weird return from a datastore, where a start + count > maxOptions
					// implies maxOptions isn't really valid and we have to go into faking it.
					//And more or less assume more if count == results.length
					if(dataObject.count == results.length){
						displayMore = true;
					}
				}
			}else if(dataObject.count == results.length){
				//Don't know the size, so we do the best we can based off count alone.
				//So, if we have an exact match to count, assume more.
				displayMore = true;
			}

			this.nextButton.style.display = displayMore ? "" : "none";
			dojo.attr(this.nextButton,"id", this.id + "_next");
			return this.domNode.childNodes;
		}
	}
);

dojo.declare(
	"epages.cartridges.de_epages.content.widget.TagEditor",
	dijit.form.ComboBox,
	{
		templateString: '',
		templatePath: dojo.moduleUrl('epages.cartridges.de_epages.content.widget',"templates/TagEditor.html"),
		pageSize : 5, // old numberOfEntries

		/**
		 * public properties
		 */

		// this can be used to group multiple TagEditors on the same page
		// if a tag is added, then all TagEditors in the same group will
		// be updated with the new tag
		group:              "", // string - name of all tag inputs which share the same tags
		presetTags:      	"", // string - use this to preset Tags, seperate with ","
		value:              "", // string[]
		name:               "Tags", // string
		delimiter:			',', // string - tagseperator
		placeholder: '',

		description:        '', // string

		// hasDownArrow: [const] Boolean
		//		Set this textbox to have a down arrow button, to display the drop down list.
		//		Defaults to true.
		hasDownArrow: false,

		// searchDelay: Integer
		//		Delay in milliseconds between when user types something and we start
		//		searching based on that value
		searchDelay: 100,
		style: '',
		labelType: "html",

		storeType : "epages.cartridges.de_epages.content.widget._TagEditorStore",
		/**
		 * private properties
		 */

		_editAtLastEl  :    true, // is the user edition the last tag in the taglist
		_activeTagIndex:    null, // current working tag

		postMixInProperties: function() {

			this.store = {"dummy":"dummy"};
			this.inherited("postMixInProperties",arguments);
			this.baseClass="";
		},
		postCreate: function() {
			// summary: create store, read preset tags, set input value, subscribe events
			// needs to be done BEFORE inherited

			// read preset tags
			if (typeof(this.presetTags) == "string") {
				this.presetTags = this._splitTags(this.presetTags);
			} else {
				this.presetTags = $A();
			}
			// add preset to value group
			var valueGrouped = parent.epages.cartridges.de_epages.content.widget.valueGrouped;
			// added compatiblity to previous tageditor
			if(dojo.isArray(valueGrouped[this.group])){
				this.presetTags.merge(valueGrouped[this.group]);
				valueGrouped[this.group] = undefined;
			}
			// create tag source
			if (valueGrouped[this.group] === undefined) {
				valueGrouped[this.group] = new(eval(this.storeType))(this.id);
			}
			if (this.presetTags.length) {
				valueGrouped[this.group].mergeIn(this.presetTags);
			}
			if (typeof(this.value) == "string") {
				this.value = this._splitTags(this.value);
			}
			this.store = valueGrouped[this.group];

			// remove dojo magic, someone somewhere deep in dojo sets an id on the input
			this.focusNode.removeAttribute("id");
			this.focusNode = new epages.widget.FormElement({}, this.focusNode).elementNode;
			if (this.delimiter) {
				this.focusNode.value = this.value.join(this.delimiter + ' ');
			} else if(this.value.length){
				this.focusNode.value = this.value[0];
			} else {
				this.focusNode.value = "";
			}
			this.inherited("postCreate",arguments);

			if(this.description.length > 1) {
				this.focusNode.setAttribute("description", this.description);
			}
			//
			this._updatevalue();
			this.connect(this.focusNode, "onkeyup", "_evaluateInput");
			epages.topDojo.subscribe('Createobjectbutton/objectCreated',this,"hide");
			epages.topDojo.subscribe('Createobjectbutton/closed',this,"hide");

			dojo.forEach(dojo.query('.dijitPlaceHolder',this.domNode),function(item){
				dojo.destroy(item);
			});
		},
		_getQueryString: function(/*String*/ text){
			// overwrite dijit.form.ComboBoxMixin
			// summary: add trim
			return dojo.string.trim(dojo.string.substitute(this.queryExpr, [text]));
		},
		_showResults: function(/*String*/ key,/*Sting?*/lastTyped){
			// - changed popup class
			// - set _activeTagIndex, _editAtLastEl
			if (this.delimiter) {
				var tagList = this.focusNode.value;
				var caretPosition = this._getCaretPos(this.focusNode);
				var countTags = tagList.substr(0, caretPosition).match(new RegExp(this.delimiter, 'g'));
				this._activeTagIndex = countTags ? countTags.length : 0;
				if (!tagList.substr(caretPosition).match(new RegExp(this.delimiter, 'g'))) {
					this._editAtLastEl = true;
				}
				else {
					this._editAtLastEl = false;
				}
			}
			if(!this._popupWidget){
				// create the suggest popup
				var popupId = this.id + "_popup";
				this._popupWidget = new epages.cartridges.de_epages.content.widget._TagEditorMenu({
					onChange: dojo.hitch(this, function(evt){
						this._selectOption(evt);
						if (this.delimiter && evt.target != this._popupWidget.nextButton &&
							evt.target != this._popupWidget.previousButton && this._editAtLastEl) {
							// if last tag is edited and tag is selected add delimitter
							this.focusNode.value = this.focusNode.value + this.delimiter+" ";
						}
					}),
					id: popupId
				});
				dijit.removeWaiState(this.focusNode,"activedescendant");
				dijit.setWaiState(this.focusNode,"owns",popupId); // associate popup with focusNode
			}
			// create a new query to prevent accidentally querying for a hidden
			// value from FilteringSelect's keyField
			var query = dojo.clone(this.query); // #5970
			this._lastInput = lastTyped ? lastTyped : key; // Store exactly what was entered by the user.
			this._lastQuery = query[this.searchAttr] = this._getQueryString(key);
			// #5970: set _lastQuery, *then* start the timeout
			// otherwise, if the user types and the last query returns before the timeout,
			// _lastQuery won't be set and their input gets rewritten
			this.searchTimer=setTimeout(dojo.hitch(this, function(query, _this){
				this.searchTimer = null;
				var fetch = {
					queryOptions: {
						ignoreCase: this.ignoreCase,
						deep: true
					},
					query: query,
					onBegin: dojo.hitch(this, "_setMaxOptions"),
					onComplete: dojo.hitch(this, "_openResultList"),
					onError: function(errText){
						_this._fetchHandle = null;
						console.error('dijit.form.ComboBox: ' + errText);
						dojo.hitch(_this, "_hideResultList")();
					},
					start: 0,
					count: this.pageSize,
					origKey: key,
					tagEditorId: this.id // exclude tags of own id
				};
				dojo.mixin(fetch, _this.fetchProperties);
				this._fetchHandle = _this.store.fetch(fetch);

				var nextSearch = function(dataObject, direction){
					dataObject.start += dataObject.count*direction;
					// #4091:
					//		tell callback the direction of the paging so the screen
					//		reader knows which menu option to shout
					dataObject.direction = direction;
					this._fetchHandle = this.store.fetch(dataObject);
				};
				this._nextSearch = this._popupWidget.onPage = dojo.hitch(this, nextSearch, this._fetchHandle);
			}, query, this), this.searchDelay);
		},
		_startSearch: function(/*String*/ key,/*Sting?*/lastTyped){
			// overwrite dijit.form.ComboBoxMixin
			this._showResults(key,lastTyped);
		},
		_announceOption: function(/*Node*/ node){
			// summary:
			//		a11y code that puts the highlighted option in the focusNode.
			//		This way screen readers will know what is happening in the
			//		menu.
			// overwrite dijit.form.ComboBoxMixin
			if(!node){
				return;
			}
			// pull the text value from the item attached to the DOM node
			var newValue;
			if( node == this._popupWidget.nextButton ||
				node == this._popupWidget.previousButton){
				this.item = undefined;
				this.value = '';
			}else{
				newValue = this.inputLabelFunc(node.item, this.store);
				// get the text that the user manually entered (cut off autocompleted text)
				this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length);
				// set up ARIA activedescendant
				dijit.setWaiState(this.focusNode, "activedescendant", dojo.attr(node, "id"));
				// autocomplete the rest of the option to announce change
				this._autoCompleteText(newValue);
			}
		},
		_autoCompleteText: function(/*String*/ text){
			// overwrite dijit.form.ComboBoxMixin
			var fn = this.focusNode;
			// IE7: clear selection so next highlight works all the time
			var oldCpos = this._getCaretPos(fn);
			dijit.selectInputText(fn, fn.value.length);
			// does text autoComplete the value in the focusNode?
			var newCpos = this._getCaretPos(fn);
			// only try to extend if we added the last character at the end of the input
			if((newCpos+1) > fn.value.length){
				// only add to input node as we would overwrite Capitalisation of chars
				// actually, that is ok
				var tagList = fn.value;
				if (this.delimiter) {
					var tlSplit = tagList.split(this.delimiter);
					if(tlSplit.length>this._activeTagIndex){
						// replace old with new tag but keep leading and trailing spaces
						tlSplit[this._activeTagIndex] = tlSplit[this._activeTagIndex].replace(/^(\s*).*(\s*)$/,"$1"+text+"$2");
						// calculate chars to select
						var selectEnd = tlSplit.slice(0,this._activeTagIndex+1).join(this.delimiter).length;
						fn.value = tlSplit.join(this.delimiter);
						dijit.selectInputText(fn, oldCpos,selectEnd);
					}
				} else {
					fn.value = tagList.replace(/^(\s*).*(\s*)$/,"$1"+text+"$2");
					dijit.selectInputText(fn, oldCpos);
				}

			}
		},

		_startSearchFromInput: function(){
			// overwrite dijit.form.ComboBoxMixin
			// fix for multi tag
			// calculate tag which is currently under edit
			var tagList = this.focusNode.value;
			var caretPosition = this._getCaretPos(this.focusNode);
			var tag = this._getTagAt(caretPosition, tagList);
			this._startSearch(tag.replace(/([\\\*\?])/g, "\\$1"),this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
		},
		_startSearchAll: function(){
			// overwrite dijit.form.ComboBoxMixin
			// fix for multi tag
			this._startSearch('',this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
		},
		_getTagAt: function(/*int*/position, /*string*/tagList) {
			// summary: recieve the tag from the position of the caret
			// return string
			if (this.delimiter) {
				var leftPos = tagList.substr(0, position).lastIndexOf(this.delimiter);
				leftPos = (leftPos == -1 ? 0 : leftPos + 1);
				var rightPos = tagList.substr(position).indexOf(this.delimiter);
				rightPos = (rightPos == -1 ? tagList.length : rightPos + position);
				return tagList.substring(leftPos, rightPos);
			} else {
				return tagList;
			}
		},
		_evaluateInput: function(evt) {
			// summary: handle key input
			this._updatevalue();
			if (this.delimiter && evt.keyCode == dojo.keys.ENTER && this._editAtLastEl) {
				// add delimiter if edited tag was the last one
				this.focusNode.value = this.focusNode.value + this.delimiter+" ";
			}
		},
		_updatevalue: function() {
			// summary: set the current value to make it accessable
			this.value = this._splitTags(this.focusNode.value);
			this.store.setValue(this.id, this.value);
			dojo.publish(this.id + "/onChange", [{ tags: this.value }]);
		},

		_splitTags: function(/*String*/tagList) {
			// summary: split and trim tagList on delimiter
			// return $A
			var finalTagArray = [];
			if (this.delimiter) {
				var tagArray = tagList.split(this.delimiter);
				for( var i=0,iLength=tagArray.length ; i<iLength ; i++ ) {
					var el = tagArray[i];
					el = el.replace(/^\s*/, "");
					el = el.replace(/\s*$/, "");
					if (el != "") {
						finalTagArray.push(el);
					}
				}
			} else {
				tagList = tagList.replace(/^\s*/, "");
				tagList = tagList.replace(/\s*$/, "");
				if (tagList != "") {
					finalTagArray.push(tagList);
				}
			}
			return $A(finalTagArray);
		},

		getValue: function() {
			return this.focusNode.value;
		},

		setValue: function(newValue) {
			if(newValue) {
				this.value = this.focusNode.value = newValue;
			} else {
				this.value = this.focusNode.value = '';
			}
		},

		hide: function(){
			this._hideResultList();
		},

		labelFunc: function(/*item*/ item, /*dojo.data.store*/ store){
			var itemvalue = store.getValue(item, this.searchAttr);
			var result;
			if(typeof(itemvalue) == 'object') {
				result = itemvalue.labelvalue;
			} else {
				result = itemvalue; // String
			}

			// beautify
			if(this.focusNode.value) {
				var fnv = this.focusNode.value; // 'de'
				var regex = new RegExp(fnv, "g");
				result = result.replace(regex, '<strong>'+fnv+'</strong>'); // 'de_asdasfdasdf'
			}
			return result;
		},

		inputLabelFunc: function(/*item*/ item, /*dojo.data.store*/ store){
			var itemvalue = store.getValue(item, this.searchAttr);
			if(typeof(itemvalue) == 'object') {
				return itemvalue.inputvalue;
			}
			return itemvalue; // String
		}
	}
);

dojo.declare("epages.cartridges.de_epages.content.widget._TagEditorStore", null, {
	availableTags: $A(),
	currentValues: {},
	constructor: function(){},

	mergeIn : function(/*String[]E*/tags){
		// summary: add tags to preset tags
		this.availableTags.merge(tags);
		this.availableTags.unique();
	},
	setValue : function(/*String*/id,/*String[]*/tags){
		// summary: set value of one of the tageditors of the group
		if (tags.length) {
			// prevent empty tags
			this.currentValues[id] = tags;
		}
	},
	getValue: function(	/* item */ item,
						/* attribute-name-string */ attribute,
						/* value? */ defaultValue){
		return item;
	},

	isItemLoaded: function(/* anything */ something){
		return true;
	},

	getFeatures: function(){
		return {"dojo.data.api.Read": true, "dojo.data.api.Identity": true};
	},

	_fetchItems: function(	/* Object */ args,
							/* Function */ findCallback,
							/* Function */ errorCallback){
		// summary:
		//		See dojo.data.util.simpleFetch.fetch()
		if(!args.query){ args.query = {}; }
		if(!args.query.name){ args.query.name = ""; }
		if(!args.queryOptions){ args.queryOptions = {}; }
		// merge preset tags and tags from the editors
		var tags = $A();
		for(var v in this.currentValues){
			if (v != args.tagEditorId) {
				tags.merge(this.currentValues[v]);
			}
		}
		tags.merge(this.availableTags);
		tags.unique();
		var matcher = dojo.data.util.filter.patternToRegExp(args.query.name, args.queryOptions.ignoreCase),
		items = tags.match(matcher);
		if(args.sort){
			items.sort(dojo.data.util.sorter.createSortFunction(args.sort, this));
		}
		findCallback(items, args);
	},

	close: function(/*dojo.data.api.Request || args || null */ request){
		return;
	},

	getLabel: function(/* item */ item){
		return item.innerHTML;
	},

	getIdentity: function(/* item */ item){
		return item;
	}
});
//Mix in the simple fetch implementation to this class.
dojo.extend(epages.cartridges.de_epages.content.widget._TagEditorStore,dojo.data.util.simpleFetch);