var $ = function(id){ return YAHOO.util.Dom.get(id); };
var BTO = function(){
	return{
		getJSON:function(responseText){
			responseText = responseText.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1").toString();
			
			var json = YAHOO.lang.JSON.parse(responseText);
			
			//Hack to make sure we work in compliance with YUI
			json.content = json.content.replace( /\\'/g, "'" );
			json.title = json.title.replace( /\\'/g, "'" );
			
			if( json.members )
				json.members = json.members.replace( /\\'/g, "'" );
			
			return json;
		}
	};
}();

var UI = function(){
	return{
		getAncestorByClass:function(node,className){
			while( node ){
				if( YAHOO.util.Dom.hasClass(node,className) )
					return $(node);
				else if( node.parentNode )
					node = node.parentNode;
				else
					return null;
			}
			
			//return $(document);
		},
		getAncestorByNodeName:function(node,name){
			name = name.toLowerCase();
			while( node ){
				if( node.nodeName.toLowerCase() == name )
					return $(node);
				else if( node.parentNode )
					node = node.parentNode;
				else
					return null;
			}
		}
	};
}();


UI.ajax = function(){
	var RequestIDString = "__requestID";
	
	var ajaxEventKey = "__ajaxEvent";
	var authentication;
	var requestIndex = 0;
	var requests = {};
	
	var defaultFailure = function(o){
		alert(o);
	};
	
	var forceAuthentication = function(json){
		var isNew = false;
		
		var header = "Authenticate Credentials";
		
		if( !authentication ){
			isNew = true;
			
			authentication = new YAHOO.widget.Panel("test",
				{	width:"500px",
					fixedcenter:true,
					close:false,
					draggable:false,
					zindex:10,
					modal:true	}
			);
		}
		
		authentication.setHeader(header);
		authentication.setBody(json.content);
		if( isNew )
			authentication.render(document.body);
		else
			authentication.show();
	};
	
	var makeRequest = function(urlString, params, failure, success, node, theIndex, additionalUIParams){
		if( failure == null )
			failure = defaultFailure;
		
		var callback = {
			failure:failure,
			success:function(o){
				try{
					var json = BTO.getJSON(o.responseText);
					
					/*
					//New exception detail
					if( json.isException ){
						alert( json.extendedInfo || "A system error has occurred and the administrators have been notified." );
						//success.call(null,json,requests[json.requestID]);
						return;
					}
					*/
					
					if( json.isAuthenticated ){
						removeAuthentication();
						success.call(null,json,requests[json.requestID]);
						//Should we clean up now? possibly...
						requests[json.requestID] = null;
					}else{
						forceAuthentication(json);
					}
				}catch(e){
					//Non-JSON response!
					//alert(e);
					document.body.innerHTML = e + "<br/>" + o.responseText;
				}
			}
		};
		
		if( !theIndex ){
			var index = requestIndex++;
			params[ RequestIDString ] = index;
			params[ ajaxEventKey ] = 1;
			
			requests[ index ] = {
				id:index,
				call:YAHOO.util.Connect.asyncRequest( 'POST', urlString, callback, queryParamify(params) ),
				node:node,
				params:additionalUIParams
			};
		}else{
			params[ RequestIDString ] = theIndex;
			YAHOO.util.Connect.asyncRequest( 'POST', urlString, callback, queryParamify(params) );
		}
	};
	
	var queryParamify = function(hash,separator){
		var ret = "";
			
		separator = separator || "&";
		
		for( var key in hash ){
			ret += (ret==""?"":separator) + encodeURIComponent(key) + "=" + encodeURIComponent(hash[key]);
		}
		
		return ret;
	};
	
	var removeAuthentication = function(){
		if( authentication ){
			authentication.setBody("");
			authentication.hide();
		}
	};
	
	return{
		authenticate:function(theForm,requestID){
			theForm.login.disabled = true;
		},
		deleteAllRequests:function(index){
			requests = {};
			requestIndex = 0;
		},
		makeRequest:function( eventName, params, failure, success, node, theIndex, additionalUIParams ){
			makeRequest( "?event="+eventName, params, failure, success, node, theIndex, additionalUIParams );
		},
		makeURLRequest:function( urlString, params, failure, success, node, theIndex ){
			makeRequest( urlString, params, failure, success, node, theIndex );
		},
		queryParamify: function(hash,separator){
			return queryParamify(hash,separator);
		},
		setForm:function( oForm ){
			YAHOO.util.Connect.setForm(oForm);
		}
	};
}();

UI.workspace = function(){
	return{
		cleanUp:function(node){
			if( node.parentNode )
				node.parentNode.removeChild(node);
		},
		create:function(nodeType,content,oStyle,className){
			var created = document.createElement(nodeType);
			
			created.style.left = "-1000px";
			created.style.postion = "absolute";
			created.style.visibility = "hidden";
			for( var p in oStyle )
				created.style[p] = oStyle[p];
			created.innerHTML = content;
			document.body.appendChild(created);
			
			created.className = className;
			
			return created;
		}
	};
}();

UI.data = function(){
	var animationTime = .2;
	var classAdd = "Add";
	var classData = "RecordData";
	var classForm = "RecordForm";
	var classLoading = "Loading";
	var classRecord = "Record";
	var classRecordContainer = "Records";
	var keyAnim = "_anim";
	var keyCache = "_cache";
	var keyForm = "_form";
	var keyLoading = "_isLoading";
	var keyMask = "_mask";
	var keyState = "_state";
	var keyUpdate = "_update";
	
	var cache = function(node){
		node[keyCache] = node.innerHTML;
	};
	
	var deleteSuccess = function(json,request){
		var node = request.node;
		if(node)
			remove(node);
			
		updateUI(node);
	};
	
	var display = function(json,request){
		var node = request.node;
		if( node[keyLoading] ){
			node[keyUpdate] = json.content;
		}else{
			update(node,json.content);
		}
	};
	
	/*
	var bubbleSort = function(inputArray, start, rest){
		for (var i = rest - 1; i >= start;  i--){
			for (var j = start; j <= i; j++) {
				if (inputArray[j+1] < inputArray[j]) {
					var tempValue = inputArray[j];
					inputArray[j] = inputArray[j+1];
					inputArray[j+1] = tempValue;
			  }
		   }
		}
		return inputArray;
	}
	*/
	
	var displayAndSort = function(json,request){
		var node = request.node;
		display(json,request);
		
		//Because of all the animations and stuff, makes this very difficult to do ... should we proceed?
	};
	
	var displayForm = function(json,request){
		display(json,request);
		//Add submit handlers?
	};
	
	var displayLoading = function(type){
		var node = this.getEl();
		
		//Display the loading stuff
		node.innerHTML = "<div class='Loading' style='height:100%;'>...loading...</div>";
		
		var anim = new YAHOO.util.Anim(node, {opacity:{to:1}}, animationTime);
		anim.onComplete.subscribe( function(){ node[keyLoading] = null; update(node); } );
		anim.animate();
	};
	
	/*
		When we want to show the loading stuff, we want to perform some effects to make this visually stunning.
	*/
	var load = function(node){
		node[keyLoading] = true;
		var fade = new YAHOO.util.Anim(node, { opacity:{to:0} }, animationTime);
		fade.onComplete.subscribe( displayLoading );
		fade.animate();
	};
	
	var mask = function(node){
		if( !node[keyMask] ){
			var div = document.createElement("div");
			
			div.className = "Mask";
			
			YAHOO.util.Dom.setStyle( div, "opacity", ".4" );
			
			node.appendChild(div);
			node[keyMask] = div;
		}else{
			YAHOO.util.Dom.setStyle( node[keyMask], "display", "" );
		}
	};
	
	var remove = function(node){
		var anim = new YAHOO.util.Anim(node, {opacity:{to:0},height:{to:0}}, animationTime);
		anim.onComplete.subscribe( function(){
			var container = UI.getAncestorByClass(node,classRecordContainer),
				visibility = false;
			
			if( container ){
				//We haven't removed the record yet, so it exists
				visibility = YAHOO.util.Dom.getElementsByClassName( classRecord, null, container ).length == 1;
			}
			
			updateNoItemsVisibility(node,visibility);
			
			node.parentNode.removeChild(node);
		} );
		anim.animate();
	};
	
	var saveSuccess = function(json,request){
		display(json,request);
		updateUI();
	};
	
	var update = function(node,content){
		content = String(content || node[keyUpdate]).replace(/^\s+|\s+$/g,"");
		node[keyUpdate] = null;
		
		var region = YAHOO.util.Dom.getRegion( node );
		
		var origHeight = region.bottom-region.top;
		div = UI.workspace.create("div",content,{width:(region.right-region.left)+"px"},node.className);
		
		region = YAHOO.util.Dom.getRegion(div);
		UI.workspace.cleanUp(div);
		
		var anim = new YAHOO.util.Anim( node, {opacity:{to:0},height:{to:region.bottom-region.top}},animationTime);
		anim.onComplete.subscribe( function(){ node.innerHTML = content; node.style.height = null; var anim2 = new YAHOO.util.Anim(node,{opacity:{to:1}},animationTime); anim2.animate(); } );
		anim.animate();
	};
	
	var updateNoItemsVisibility = function(node, visibility){
		var container = UI.getAncestorByClass( node, classRecordContainer );
		
		var noItems = YAHOO.util.Dom.getElementsByClassName( "NoItems", null, container );
		if( noItems.length == 1 ){
			noItems[0].style.display = ( visibility ? "" : "none" );
		}
	};
	
	return{
		checkForOther:function(oSel,aOther){
			var id = oSel.options[ oSel.selectedIndex ].value,
				isOther = false;
			
			for( var i = 0; i < aOther.length; i++ ){
				if( id == aOther[i] ){
					isOther = true;
					break;
				}
			}
			
			var td = UI.getAncestorByNodeName(oSel,"td");
			if( !td ){
				return;
			}
			
			var other = YAHOO.util.Dom.getElementsByClassName( "Other", null, td );
			if( other.length > 0 ){
				other = other[0];
				
				if( other[keyAnim] ){
					var anim = other[keyAnim];
					anim.stop();
					
					var anim = new YAHOO.util.Anim( other, {opacity:{to: ( isOther ? 1 : 0 )}}, animationTime );
					anim.onComplete.subscribe( function(){ other[keyAnim] = null; } );					
					other[keyAnim] = anim;
					anim.animate();
				}else{
					//create the animation
					var anim = new YAHOO.util.Anim( other, {opacity:{to: ( isOther ? 1 : 0 ) } }, animationTime );
					anim.onComplete.subscribe( function(){ this.getEl()[keyAnim] = null; } );					
					other[keyAnim] = anim;
					anim.animate();
				}
			}
		},
		deleteObject:function(node,event,id,actualRecord){
			var c = actualRecord || classRecord;
			
			var record = UI.getAncestorByClass( node, c );
			
			if( id != 0 ){
				UI.ajax.makeRequest( event, {id:id}, null, deleteSuccess, record );
			}else{
				remove(record);
			}
		},
		edit:function(node,event,id,additionalClassNames,additionalArgs,ignoreIdConstraint){
			var record;
			
			if( id != 0 || ignoreIdConstraint){
				record = UI.getAncestorByClass( node, classRecord );
			}else{
				var container = UI.getAncestorByClass( node, classRecordContainer );
				
				if( container ){
					record = document.createElement("div");
					record.className = classRecord;
					
					YAHOO.util.Dom.addClass( record, additionalClassNames );
					
					var add = YAHOO.util.Dom.getElementsByClassName( classAdd, null, container );
					
					if( add.length == 0 )
						container.appendChild(record);
					else
						container.insertBefore(record,add[add.length-1]);
				}
				
				//Hide NoItems
				updateNoItemsVisibility( container, false );
			}
			
			if( record ){
				load(record);
				
				var params = {};
				additionalArgs = additionalArgs || {};
				for( var p in additionalArgs )
					params[p] = additionalArgs[p];
				
				params.id = id;
				
				UI.ajax.makeRequest( event, params, null, displayForm, record );
			}
		},
		editSub:function(node,event,id,parentID,additionalClassNames){
			var record = UI.getAncestorByClass( node, classRecord );
			
			if(record){
				cache(record);
				this.edit(node,event,id,additionalClassNames,{parentID:parentID},true);
			}
		},
		get:function(node,event,id){
			var record = UI.getAncestorByClass( node, classRecord );
			if( record ){
				if( id != 0 || record[keyCache] ){
					load(record);
					
					if( !record[keyCache] ){
						UI.ajax.makeRequest( event, {id:id}, null, display, record );
					}else{
						record[keyUpdate] = record[keyCache];
						record[keyCache] = null;
					}
				}else{
					var container = UI.getAncestorByClass( node, classRecordContainer ),
						visibility = true;
					
					if( container ){
						//We haven't removed the record yet, so it exists
						visibility = YAHOO.util.Dom.getElementsByClassName( classRecord, null, container ).length == 1;
					}
					
					updateNoItemsVisibility( record, visibility );
					remove(record);
				}
			}
		},
		loadHTML:function(node,event,params,resultNodeClass){
			var resultNode = node;
			if( !!resultNodeClass ){
				var resultNode = YAHOO.util.Dom.getElementsByClassName( resultNodeClass, null, node );
				if( resultNode.length > 0 ){
					resultNode = resultNode[0];
				}else{
					resultNode = document.createElement("div");
					resultNode.className = resultNodeClass;
					node.appendChild(resultNode);
				}
			}
			
			var onComplete = display;
			load(resultNode);
			UI.ajax.makeRequest( event, params, null, onComplete, resultNode );
		},
		remove:function(node,parentClass){
			if( !!parentClass )
				node = UI.getAncestorByClass(node,parentClass);
			
			if( node )
				remove(node);
		},
		save:function(node,event,updateFunction,additional){
			var onComplete = saveSuccess,
				updateFunction = updateFunction || "";
			switch(updateFunction.toLowerCase()){
				case "sort":
					//additional = sortArguments
					additional = additional || "sort1,sort2";
					onComplete = displayAndSort;
					break;
				default:
					break;
			}
			
			var record = UI.getAncestorByClass( node, classRecord );
			if( record ){
				record[keyCache] = null;
				
				load(record);
				UI.ajax.setForm(node);
				UI.ajax.makeRequest( event, {}, null, onComplete, record, null, additional );
			}
		}
	};
}();
var updateUI = function(node){};