// ####################################################################
// ####################################################################
// ## CLASS : DeskPRO.TicketCat
// ####################################################################
// ####################################################################

/**
 * TicketCat class takes care of auto-loading
 * subcats when a parent cat was selected. Also allows
 * custom callbacks to be added to listen to the change events.
 *
 * @param {Object} cathtml The category HTML for each parent
 * @param {Object} options Options
 */
DeskPRO.TicketCat = function(cathtml, options) {
	this.options = {
		parentSelId: 's_category',
		subSelId: 's_subcategory',
		containSubId: 'contain_sub',
		classSubToggle: 'subsel_toggle'
	};

	$.extend(this.options, options);

	this.catHTML = cathtml;

	this.parentSelEl = $('#' + this.options.parentSelId).get(0);
	this.containSub = $('#' + this.options.containSubId).get(0);

	this.catChangedCallbacks = [];

	$(this.parentSelEl).bind(
		'change',
		{instance: this},
		function(event) {
			event.data.instance.parentSelChanged(event.target);
		}
	);

	$('#' + this.options.subSelId).bind(
		'change',
		{instance: this},
		function(event) {
			event.data.instance.subSelChanged(event.target);
		}
	);
};





/**
 * This method handles when the parent select box has been changed. It
 * will load up the subcats and reveal it if there are any, and hide it
 * if there are none. After this is done, any registered callbacks
 * will also be fired.
 *
 * Note that this toggles all elements with the
 * class in options.classSubToggle. You can use this top show/hide
 * elements with the select box itself (ie. a container or a table).
 *
 * @param {HTMLElement} The select element that fired the change
 */
DeskPRO.TicketCat.prototype.parentSelChanged = function(sel) {

	var parentId = sel.options[sel.selectedIndex].value;
	var subHTML = this.catHTML['parent_'+parentId];

	if (subHTML) {

		$(this.containSub).html(subHTML);

		// Need to show it
		if ($(this.containSub).css('display') == 'none') {
			$(this.containSub).css('display', '');
			$('.' + this.options.classSubToggle).css('display', '');
		}

		// Assign listener
		var subsel = $('#' + this.options.subSelId).bind(
			'change',
			{instance: this},
			function(event) {
				event.data.instance.subSelChanged(event.target);
			}
		).get(0);

		this.performCallbacks(parentId, 'parent', true);

		this.subSelChanged(subsel);

	} else {

		// Hide it if we need to
		if ($(this.containSub).css('display') != 'none') {
			$(this.containSub).css('display', 'none');
			$('.' + this.options.classSubToggle).css('display', 'none');
		}

		this.performCallbacks(parentId, 'parent', false);
	}
};





/**
 * This method handles when the sub cateory is changed. The only
 * functionality it actually does is fire any registered callbacks.
 *
 * @param {HTMLElement} The select that fired the change
 */
DeskPRO.TicketCat.prototype.subSelChanged = function(sel) {

	var catId = sel.options[sel.selectedIndex].value;

	this.performCallbacks(catId, 'sub');
};





/**
 * Fire all of the registered callbacks.
 *
 * @param {Integer} The new catId from the changed select
 * @param {String} 'parent' or 'sub', depending on which select is firing
 */
DeskPRO.TicketCat.prototype.performCallbacks = function (catId, type, hasSubs) {

	var num = this.catChangedCallbacks.length;

	if (!num) {
		return;
	}

	for (var i = 0; i < num; i++) {

		var callback = this.catChangedCallbacks[i];

		if (typeof callback == 'object') {
			callback.fn.call(callback.context, catId, type, hasSubs);
		} else {
			callback(catId, type, hasSubs);
		}

	}
};





/**
 * Register a callback function that fires whenever a category selection
 * changes.
 *
 * @param {Function} The function to call
 * @param {Object} (Optional) Provide an object to call the function context with.
 *                 Use this to make 'this' in the function refer to context instead of
 *                 the function itself.
 */
DeskPRO.TicketCat.prototype.addCallback = function(fn, context) {

	if (typeof context != 'undefined') {
		this.catChangedCallbacks.push({context: context, fn: fn});
	} else {
		this.catChangedCallbacks.push(fn);
	}
};














// ####################################################################
// ####################################################################
// ## CLASS : DeskPRO.TicketCatFields
// ####################################################################
// ####################################################################

/**
 * TicketCatFields works in conjunction with an instance of TicketCat.
 * It is designed to load custom fields for a particular category when
 * a category change has occurred.
 *
 * @param {TicketCat} ticketCatObj The TicketCat instance to use
 * @param {Object} options Options
 */
DeskPRO.TicketCatFields = function(ticketCatObj, options) {

	this.options = {
		containFieldsId : 'contain_fields',
		classFieldsToggle: 'fields_toggle',
		loadingClassToggle: 'fields_loading',
		ajaxScript: 'ticketform_ajax.php',
		ajaxData: {},
		ticketPerm: 'ticket_start',
		formId: false,
		forCats: false,
		cacheNamesRegex: /^custom_.*$/,
		doneLoadingCallback: false
	};

	$.extend(this.options, options);

	this.ticketCatObj = ticketCatObj;
	this.containFieldsEl = $('#' + this.options.containFieldsId).get(0);
	this.formEl = false;

	if (this.options.formId) {
		this.formEl = $('#' + this.options.formId).get(0);
	}

	this.currentAjax = false;
	this.customFieldValueCache = {};
	this.customFieldValueCacheText = {};

	this.ticketCatObj.addCallback(this.categoryChanged, this);
};





/**
 * This method is called whenever a parent or sub category selection has
 * changed. It will show ticket fields for the selected category via ajax.
 *
 * @param {Integer} The category ID of the newly selected category
 * @param {String} 'parent' or 'sub' depending on which was changed.
 */
DeskPRO.TicketCatFields.prototype.categoryChanged = function(catId, type, hasSubs) {

	if (this.currentAjax) {
		this.currentAjax.abort();
		this.currentAjax = false;
	}

	catId = parseInt(catId);

	// If it has subs, then the subselect should be calling
	// to so just do nothing
	if (type == 'parent' && hasSubs) {
		return;
	}

	// If we have forCats and catId is NOT one of
	// them then we dont need to load anything
	if (this.options.forCats && typeof this.options.forCats[catId] == 'undefined') {
		// Still need to rmeove any stale fields though
		this.showFields('');
		return;
	}

	/*************************
	* Bad cat, probably "Please Select"
	*************************/

	if (catId < 1) {
		this.showFields('');
		return;
	}

	/*************************
	* Send ajax request to get fields
	*************************/

	var sendData = $.extend(
		{catid: catId, perm: this.options.ticketPerm},
		this.options.ajaxData,
		this.getDefaultFormValues()
	);

	// Hide existing fields if any
	this.showFields();

	// Show loading div
	$('.' + this.options.loadingClassToggle).show();

	this.currentAjax = $.ajax({
		type: 'POST',
		complete: DeskPRO.createDelegate(this.on_ajaxComplete, this),
		dataType: 'text',
		data: sendData,
		url: this.options.ajaxScript
	});
};





/**
 * This function returns a cache of cusotm field input the user
 * has entered so when the custom fields are replaced with a change, their
 * input isn't lost.
 *
 * @return {Object} A map of field:values
 */
DeskPRO.TicketCatFields.prototype.getDefaultFormValues = function() {

	// None exist / none configured
	if (!this.formEl) {
		return {};
	}

	var els = $('input, textarea, select', '#' + this.options.formId);

	if (!els || !els.length) {
		return {};
	}
	
	// - Checkboxes are actually multiple input elements so they
	// are handled indivudually.
	// - They need to be sent to PHP as an array, and that means handling them
	// specially
	// - Each time we run this, we have to make sure to reset the value stored on
	// cache (we keep old ones around incase a cat doesnt have them, but switcehs to one
	// that does) is correct.
	// - This array keeps track of when it was reset so its not reset more then once
	// which would result in only sending the last selected value
	var been_reset = [];

	/*************************
	* Get field values
	*************************/

	for (var i = 0; i < els.length; i++) {
		var el = els.eq(i);

		if (!el || !el.attr('name')) {
			continue;
		}

		// Custom field names being with custom_
		if (!el.attr('name').match(this.options.cacheNamesRegex)) {
			continue;
		}

		var value = el.val();

		if (value != null) {			
			// Checkbox els
			if (el.attr('type') == 'checkbox') {
				if (!DeskPRO.inArray(el.attr('name'), been_reset)) {
					this.customFieldValueCache[el.attr('name')] = [];
				}
			
				// Will have reset it, so just dont add it if we havent checked
				if (el.is(':checked')) {
					this.customFieldValueCache[el.attr('name')].push(value);
				}
				
				
			// inputs and textareas we cache the values locally, dont need to send it along after
			} else if (el.is('input:text, textarea')) {
				
				this.customFieldValueCacheText[el.attr('name')] = value;
				
			// Everything else we send back to the server to pre-populate
			// selects, checkboxes etc
			} else {
				
				// Radios that arent checked dont matter
				if (el.attr('type') == 'radio' && !el.is(':checked')) {
					continue;
				}
				
				this.customFieldValueCache[el.attr('name')] = value;	
			}
		}
	}

	return this.customFieldValueCache;
};





/**
 * Show ticket fields in the form. This takes HTML that
 * should be entered into the form, the HTML is usually
 * the custom ticket fields.
 *
 * If there is no HTML (an empty string), then the custom fields
 * section is hidden.
 *
 * Note that this will also toggle visibility of all elements
 * that have the class in options.classFieldsToggle. Use this
 * to show/hide elements with the display of the fields. For example,
 * a container or table.
 *
 * @param {String} The HTML of the fields to insert into the form.
 */
DeskPRO.TicketCatFields.prototype.showFields = function(html) {

	html = $.trim(html);

	// No HTML means no fields, should hide the section
	if (html.length == 0) {

		if ($(this.containFieldsEl).css('display') != 'none') {
			$(this.containFieldsEl).css('display', 'none');
			$('.' + this.options.classFieldsToggle).css('display', 'none');
		}

		$(this.containFieldsEl).html('');

	// Update the HTML
	} else {

		var js = DeskPRO.getEmbeddedJS(html);
		html = DeskPRO.clearEmbeddedJS(html);

		$(this.containFieldsEl).html(html);

		if ($(this.containFieldsEl).css('display') == 'none') {
			$(this.containFieldsEl).css('display', '');
			$('.' + this.options.classFieldsToggle).css('display', '');
		}
		
		// Replace input and textarea values
		var els = $('input:text, textarea', '#' + this.options.formId);

		if (els && els.length) {
			for (var i = 0; i < els.length; i++) {
				var el = els.eq(i);
				
				if (typeof this.customFieldValueCacheText[el.attr('name')] != 'undefined') {
					el.val(this.customFieldValueCacheText[el.attr('name')]);
				}
			}
		}

		// Only eval scripts AFTER the items are in DOM
		if (js) {
			$.globalEval(js);
		}
	}
};





/**
 * This just handles when an ajax call is completed. It will take the response
 * and pass it to the showFields() method to insert into the form.
 *
 * @param {XMLHttpRequest} The XMLHttpRequest object for the ajax call
 * @param {String} Contains how the request finished
 */
DeskPRO.TicketCatFields.prototype.on_ajaxComplete = function(xhr, finishType) {

	// Hide loading div
	$('.' + this.options.loadingClassToggle).hide();

	this.currentAjax = false;
	var html = '';

	if (xhr.responseText.length) {
		html = xhr.responseText;
	}

	this.showFields(html);

	if (this.options.doneLoadingCallback) {
		this.options.doneLoadingCallback();
	}
};










// ####################################################################
// ####################################################################
// ## CLASS : DeskPRO.ajaxLoadArea
// ####################################################################
// ####################################################################

DeskPRO.User.InstantSearch = function(options) {

	this.options = {
		dataEls: '',
		dataVariable: 'data',

		resolvedDo: 'save_resolved',

		ajaxScript: '',
		ajaxData: {},

		fieldsEl: '',
		loadingEl: '',
		resultEl: '',
		resultWrapEl: '',
		articleEl: '',
		noresultWrapEl: '',

		formEl: '',
		performOnSubmit: true,
		resolveStoreId: '',
		doId: '',
		goBackFormEl: '',
		submitFormEl: '',
		
		timeout: 10000
	};

	$.extend(this.options, options);

	this.doSearch = true;

	this.lastShowId = false;

	if (this.options.performOnSubmit) {
		$(this.options.formEl).submit(DeskPRO.createDelegate(this.formSubmitted, this));
	}

	if (this.options.goBackFormEl) {
		$(this.options.goBackFormEl).click(DeskPRO.createDelegate(this.goBackForm, this));
	}

	if (this.options.submitFormEl) {
		$(this.options.submitFormEl).click(DeskPRO.createDelegate(function() { $(this.options.formEl).get(0).submit(); }, this));
		$('.btn_submit_form').click(DeskPRO.createDelegate(function() { $(this.options.formEl).get(0).submit(); }, this));
	}
};




DeskPRO.User.InstantSearch.prototype.formSubmitted = function(event) {

	var test = parseInt($('#cancel_js_submit_work').val());
	if (test) {
		return true;
	}

	event.preventDefault();
	this.doFormSearch();
};





DeskPRO.User.InstantSearch.prototype.doFormSearch = function() {

	if (!this.doSearch) {
		return true;
	}

	this.doSearch = false;
	this.performSearch();
};




DeskPRO.User.InstantSearch.prototype.goBackForm = function(event) {

	var fieldsEl = $(this.options.fieldsEl);

	$(this.options.resultWrapEl).fadeOut('fast', function() {
		fieldsEl.fadeIn('slow');
	});
	this.doSearch = true;

};


DeskPRO.User.InstantSearch.prototype.showArticle = function(article_id) {

	// Same, to slide it closed
	if (this.lastShowId == article_id) {
		var preview = $('#question_preview_' + article_id);
		var content = $('#article_content_' + article_id);

		content.slideUp('normal', function() {
			preview.slideDown();
		});

		this.lastShowId = false;
		return;
	}

	if (this.lastShowId) {
		$('#question_preview_' + this.lastShowId).show();
		$('#article_content_' + this.lastShowId).hide();
	}

	var preview = $('#question_preview_' + article_id);
	var content = $('#article_content_' + article_id);

	preview.slideUp('fast', function() {
		content.slideDown('normal');
	});

	this.lastShowId = article_id;
};


DeskPRO.User.InstantSearch.prototype.markResolved = function(article_id) {
	$(this.options.doId).val(this.options.resolvedDo);
	$(this.options.resolveStoreId).val(article_id);
	$(this.options.formEl).get(0).submit();
};





DeskPRO.User.InstantSearch.prototype.performSearch = function() {

	var loadingEl = $(this.options.loadingEl);
	var form = $(this.options.formEl).get(0);

	// Buttons with this class submit the form
	$('.btn_submit_form', loadingEl).click(function() {
		form.submit();
	});
	
	$(this.options.fieldsEl).fadeOut('normal', function() {
		loadingEl.fadeIn('fast');
	});

	var data = {};
	$.extend(data, this.getData(), this.options.ajaxData);

	$.ajax({
		url: this.options.ajaxScript,
		data: data,
		dataType: 'html',
		type: 'post',
		success: DeskPRO.createDelegate(this.ajaxSuccess, this),
		error: DeskPRO.createDelegate(this.ajaxError, this),
		timeout: this.options.timeout
	});
};





DeskPRO.User.InstantSearch.prototype.ajaxSuccess = function(data) {

	this.hideLoading();

	// No results so we can just submit the form
	if (!data.length) {
		if (this.options.noresultWrapEl.length) {
			this.showNoResult();
		} else {
			$(this.options.formEl).get(0).submit();
		}
		return;
	}

	$(this.options.resultEl).html(data).show();
	$(this.options.resultWrapEl).fadeIn();
};



DeskPRO.User.InstantSearch.prototype.ajaxError = function(ajax, status, error) {
	
	this.hideLoading();
	
	if (this.options.noresultWrapEl.length) {
		this.showNoResult();
	} else {
		$(this.options.formEl).get(0).submit();
	}
};




DeskPRO.User.InstantSearch.prototype.hideLoading = function() {
    var loadingEl = $(this.options.loadingEl);
	loadingEl.hide();

    // If the result is retrieved really fast, then
    // some browsers might not have loadingEl fully visble yet (from the fade in)
    // - Attempt to fix by trying to hide it again after a short delay
    window.setTimeout(function() { loadingEl.hide(); }, 550);
};





DeskPRO.User.InstantSearch.prototype.showNoResult = function() {

	var el = $(this.options.noresultWrapEl);
	var form = $(this.options.formEl).get(0);

	// Buttons with this class submit the form
	$('.btn_submit_form', el).click(function() {
		form.submit();
	});

	$(this.options.loadingEl).hide();
	el.fadeIn();

	// If we want a countdown, auto-submit in 5 secs
	if (el.is('.with_timer')) {
		setTimeout(
			DeskPRO.createDelegate(function() {
				$(this.options.formEl).get(0).submit();
			}, this),
			5000
		);
	}
}





DeskPRO.User.InstantSearch.prototype.getData = function() {

	if (!this.options.dataEls.length) {
		return {};
	}

	var dataEls = $(this.options.dataEls);

	if (!dataEls.length) {
		return {};
	}

	var data = [];
	for (var i = 0, len = dataEls.length; i < len; i++) {
		data.push(DeskPRO.Forms.getValue(dataEls.get(i)));
	}

	if (data.length == 1) {
		var name = this.options.dataVariable;
		data = data[0];
	} else {
		var name = this.options.dataVariable + '[]';
	}

	var ret = {};
	ret[name] = data;

	return ret;
};
