/**
 * Byng User Interface utility class
 *
 *
 * Byng UI is responsible for:
 *
 *			- DOM manipulation
 * 			- Opening, closing and updating elements
 * 			- 
 * 
 *	@author Ollie Maitland
 *	@copyright Byng Systems LLP
 */

/**
 * Framework constants
 * 
 * Holds constants which are used through-out the javascript
 * framework such as standardised DOM element IDs
 * 
 */
 
/**
 * Holds the feedback element id name
 * 
 * @type String
 */
var ELEM_FEEDBACK 	= "feedback";

/**
 * Holds the error element id name
 * 
 * @type String
 */
var ELEM_ERRORS	 	= "errors";

/**
 * Holds the popup wrapper id name
 * 
 * @type String
 */
var ELEM_POPUP_WRAP	= "popup-wrap";

/**
 * Holds the basecode version 
 * 
 * @type String
 */
var BASECODE_VERSION = "trunk";

/**
 * Holds the basecode root path
 * 
 * @type String
 */
var BASECODE_ROOT	= "/common/" + BASECODE_VERSION + "/js/mootools/";

/**
 * Represents a light UI load
 *
 * @type Int
 */
var UI_FLAG_LIGHT 		= 0;

/**
 * Represents a UI load with AJAX
 *
 * @type Int
 */
var UI_FLAG_AJAX		= 1;

/**
 * Represents a UI load with AIR
 *
 * @type Int
 */
var UI_FLAG_AIR			= 2;

/**
 * byng.ui
 * 
 */
Byng.register('byng.ui',
{
	
	/**
	 * Initialise the user interface environment
	 * 
	 */
	initialize : function ( initFlag ) 
	{
		// Set the current page
		this.page = location.pathname;
		
		// Used to track open elements
		this.tracker = new Array;
		
		// Script include tracker
		this.included = new Array (0);
		this.initFlag = initFlag;

		switch (initFlag) {
		case UI_FLAG_AJAX:

			/**
			 * Holds the byng.ui.response object
			 * 
			 * @type Byng.ui.response
			 */
			this.response = Byng.init("byng.ui.response");
			
			/**
			 * Holds the byng.ui.dom object
			 * 
			 * @type Byng.ui.dom
			 */
			this.dom = Byng.init("byng.ui.dom");
						
		break;
		}
	},
 	
 	/**
 	 * Set the initialisation requirement
 	 * 
 	 * @param Int flag
 	 */
 	setInitFlag : function (flag)
 	{	
 		this.initFlag = flag;
 	},
	
	/**
	 * Post a browser http request
	 * 
	 * Create a form in DOM and submit the request
	 * 
	 * @param ByngRequest ByngRequest
	 * 
	 */
	postAction : function (ByngRequest)
	{
		var builder = Byng.input.getBuilder();

		f = builder.formFactory (ByngRequest.composeAsString(), "postAction", ByngRequest.getAction(true))

		document.getElementsByTagName('head')[0].appendChild(f);

		f.submit();
	},
	
	/**
	 * Submit the popup form
	 * 
	 */
	submitPopup : function (form, chain) 
	{
		if (!$chk(form)) {
			form = Byng.ui.dom.getPopup().getElement('form');
		}

		switch ($type(form)) {
			case "element"	: form.submit(); /*try { form.fireEvent('submit', [form]); } catch (e) { form.submit(); }*/
			default 		: return;
		}
	},

	/**
	 * Methods to open pages
	 * 
	 * @todo Move to byng.ui.send
	 *
	 */
	
	/**
	 * Reload this window
	 * 
	 * @param Integer delay In ms
	 */
	reload : function ( delay )
	{
		delay = delay || 1000;
		setTimeout( function () { window.location.reload(); }, delay );
	},	 
	 
	/**
	 * Open a location from a ByngRequest
	 * 
	 * @param ByngRequest request
	 */
	openLocation : function (request, options)
	{
		if (!options) options = {};
		if (!options.width)  options.width = 600;
		if (!options.height) options.height = 800;
		window.open( ( $type(request) == 'object' ? request.composeAsString() : request),
													'_blank','toolbar=no' +
													',width='  + options.width +
													',height=' + options.height + 
													',resizable=yes,menubar=yes,location=no');
	},

	/**
	 * Redirect page to new location
	 *
	 * @param ByngRequest request
	 */
	gotoLocation : function (request)
	{
		window.location = ($type(request) == 'object' ? request.composeAsString(false) : request);
	},
	
	/**
	 * String encoding
	 * 
	 * The string encoding functions provide methods to 
	 * access and target DOM elements with an encoded data structure
	 * rather than nomenclature
	 * 
	 */
	 
	/**
	 * Check is a string is a valid declaration
	 * 
	 * @param String s
	 */
	isValidHex : function (s, prefix)
	{
		if (!prefix) prefix = "zz";
		if (s.substr(0,prefix.length) != prefix) return false;
		
		// remove the prefix and _ character
		h = s.substr(prefix.length+1);
		
		s = this.decodeHex (h);
		
		s = s.split("|");
		
		if (s.length < 2) return false;
					
		this.keys   = s[0].split(":");
		this.values = s[1].split(":");

		return true;
	},
	
	/**
	 * Decode a string from hexidecimal
	 * 
	 * @para String str
	 * @return String
	 */
	decodeHex : function (str)
	{
	    str = str.replace(new RegExp("s/[^0-9a-zA-Z]//g"));
	    var result = "";
	    var nextchar = "";
	    for (var i=0; i<str.length; i++){
	        nextchar += str.charAt(i);
	        if (nextchar.length == 2){
	            result += this.ntos(eval('0x'+nextchar));
	            nextchar = "";
	        }
	    }
	    return result;
	},
	
	/**
	 * Convert a number to a hexidecimal letter
	 * 
	 * @param Int n
	 * @return String
	 */
	toHex : function(n)
	{
		var digitArray = new Array('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f');
		
	    var result = ''
	    var start = true;
	    for (var i=32; i>0;){
	        i-=4;	
	        var digit = (n>>i) & 0xf;
	
	        if (!start || digit != 0){
	            start = false;
	            result += digitArray[digit];
	        }
	    }
	
	    return (result==''?'0':result);
	},
	
	/**
	 * Make hex encoded ID tag
	 * 
	 * @param Array Array keys
	 * @param Array Array values
	 * @return String
	 */
	makeIdTag : function (keys, values, prefix)
	{
		return (prefix ? prefix : 'zz') + '_' + this.encodeHex(keys,values);
	},
	
	/**
	 * Get an associative array from an ID string
	 * 
	 * @param String id 
	 */
	fromIdTag : function ( id, prefix )
	{
		if (!$chk(id)) return null;
		if ($type(id) == 'event')   id = id.target;
		if ($type(id) == 'element') id = id.getProperty('id');
		if (!$chk(prefix)) prefix = id.substring(0,id.indexOf("_"));
		if (this.isValidHex( id, prefix ) == true) {
			return this.values.associate(this.keys);
		} else {
			return null;
		}
	},
	
	/**
	 * Encode a string to a hexidecimal string
	 * 
	 * @param Array keys Array keys
	 * @param Array values Corresponding array values
	 * @return String
	 */
	encodeHex : function (keys, values)
	{
		var pipeStr = [];
		
		var pad = function (str, len, pad){
		    var result = str;		
		    for (var i=str.length; i<len; i++){
		        result = pad + result;
		    }
		    return result;
		}
		
		var encode = function (str) {
			var result = "";
		    for (var i=0; i<str.length; i++){
		        result += pad(Byng.ui.toHex(str.charCodeAt(i)&0xff),2,'0');
		    }
		    return result;
		}

		// collapse the keys		
		pipeStr.push(keys.join(":"));
		// collapse the values
		pipeStr.push(values.join(":"));		

		return encode(pipeStr.join("|"));	
	},
	
	/**
	 * Number to string
	 * 
	 * @param Int
	 * @return String
	 */
	ntos : function (n)
	{
	    n=n.toString(16);
	    if (n.length == 1) n="0"+n;
	    n="%"+n;
	    return unescape(n);
	},
	
	trim : function (s) 
	{
		if (s) { return s.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1"); } else { return ''; };
	},

	/**
	 * Toggle a DomElement between hidden and shown
	 * 
	 * @param DomElement element
	 * @param Boolean display
	 */
	toggle : function (element, display)  
	{
		element = $(element);
		if ($type(element) != "element") throw ("Toggled invalid element");
		
		// Are we using the display toggle
		if ($type(display) == 'boolean') {
			if (display == true) {

				element.setStyle('display', 'block');
			
				// If the class name is just hide then reset it in DOM
				if (element.hasClass("hide")) {
					element.removeClass('hide');
				}					
			} else {
				element.setStyle('display', 'none');
			}
			return;
		}

		// Otherwise determine from the element styles
		if (element.hasClass("hide") || element.getStyle('display') == 'none') {
			element.setStyle('display', 'block');
			
			// If the class name is just hide then reset it in DOM
			if (element.hasClass("hide")) {
				element.removeClass('hide');
			}				
		} else {
			element.setStyle('display', 'none');
		}
				
	},
	
	/**
	 * Select an element from the DOM
	 * 
	 * @param Mixed selector
	 */
	ge : function ( selector, prefix )
	{
		if (typeof selector == "object") {
			var keys = [];
			var values = [];
			$each(selector, function(v,k){
				keys.push(k);
				values.push(v);
			});
			return $(this.makeIdTag(keys,values,prefix));
		} else {
			return $(selector);
		}
	}
	
});

