/**
 * @author Dewey
 * @date 2008-03-18
 * 
 * This file establishes javascript versions of some data structure objects used in PHP form on the server-side.
 * For insight into this manner of mimicking classical class/object inheritance and other things, see:
 * 	http://javascript.crockford.com/inheritance.html and the other javascript articles on that website.
 *  See also: http://yuiblog.com/blog/2006/11/27/video-crockford-advjs/
 */

//Parasitic Inheritance:
/*
function gizmo(id) {
    return {
        id: id,
        toString: function () {
            return "gizmo " + this.id;
        }
    };
}

function hoozit(id) {
    var that = gizmo(id);
    that.test = function (testid) {
        return testid === this.id;
    };
    return that;
}
*/

//Define treeNode and collapseNode objects according to a "parasitic" inheritance model

//A node object in an unordered tree
//Definition and "Constructor":
function treeNode(DOMID, parent){
	return {
		parentNode: parent,
		DOMID: DOMID,
		childNodes: new Array(),
		
		getParent: function(){
			return this.parentNode;
		},
		setParent: function(parent_node) {
			this.parentNode = parent_node;
		},
		getChildren: function(){
			return this.childNodes;
		},
		addChild: function(child_node){
			this.childNodes[this.childNodes.length] = child_node;
		},
		newChild: function(){
			var the_new_child = treeNode(this);
			this.childNodes[this.childNodes.length] = the_new_child;
			return the_new_child;
		},
		getMyDOMID: function(){
			return this.DOMID;
		}
	}
		//	treeNode.prototype.deleteChild = function(childIndex) {
		//If index is valid, delete corresponding child
		//This involves traversing the subtree of which the child is the root, deleting each node as you return upwards, and finally deleting
		//	the child itself.  And by deletion, I mean removal from parent's child list, as well as unsetting/destruction of object.
		//	}
}
// <-- End "class" treeNode
	
	
//A "collapsable" node object that can also possess varying degrees of "freshness."
//Definition and "Constructor":
function collapseNode(DOMID, parent, last_update, freshness, collapsed_status){
	//Create a treeNode object, pass it the things it's prepared to accept, and then proceed to tack on a whole bunch of other stuff
	//	that makes us as a collapse node special.  This includes additional attributes as well as a whole raft of methods.
	var that = treeNode(DOMID, parent);
	that.last_update = last_update;
	that.freshness = freshness;
	that.collapsed_status = collapsed_status;
	
	//"Methods":
	
	//This function is called when a change has occurred involving the freshness of a node at a lower level in the tree
	that.bellAirBubble = function(positivity){
		//"Positive" means that a node beneath us is newly fresh (it could be a prevously existing node or an entirely new one)
		if (positivity == "positive") {
			//If we were stale, the fresh node that is now somewhere beneath us requires us to be "bell_air" instead
			if (this.freshness == "stale") {
				this.setFreshness("bell_air");
				//In this case, we need to pass the message on to our ancestor nodes in case they too are stale and need to update their
				//	freshness to reflect the newly fresh, instigator descendant node.
				if ((typeof(this.parentNode) != "string") && ("bellAirBubble" in this.parentNode)) {
					this.parentNode.bellAirBubble("positive");
				}
			}
		}
		//"Negative" means that a node beneath us is newly stale (it may prevously have been either "fresh" or "bell_air")
		else 
			if (positivity == "negative") {
				//If we were "bell_air," the formerly fresh node somewhere beneath us (which is now stale or gone) requires us to check to see 
				//	whether we still have reason to be "bell_air."  
				//	I.e., was the instigator descendant node our only fresh descendant, or do we have others?
				if (this.freshness == "bell_air") {
					if (this.bellAirLookDown() == false) {
						this.setFreshness("stale");
						//In this case, we need to pass the message on to our ancestor nodes in case they too are bell_air AND need to update 
						//	their freshness to reflect the staleness or disappearance of their last fresh descendant node.
						if ((typeof(this.parentNode) != "string") && ("bellAirBubble" in this.parentNode)) {
							this.parentNode.bellAirBubble("negative");
						}
					}
				}
			}
			else {
				throw new Exception("function 'bellAirBubble' was called with an invalid 'positivity' parameter:" + positivity + ".  This parameter must be either 'positive' or 'negative.'");
			}
		return;
	};
	
	//This function checks to see if we have any bell_air or fresh children
	//This is useful in checking to see whether we should keep our bell_air status
	that.bellAirLookDown = function(){
		for (child in this.childNodes) {
			var child_freshness = this.childNodes[child].getFreshness();
			if (child_freshness == "fresh" || child_freshness == "bell_air") {
				return true;
			}
		}
		return false;
	};
	
	that.getFreshness = function(){
		return this.freshness;
	};
	
	that.setFreshness = function(new_freshness){
		if (new_freshness.match(/fresh|stale|bell_air/)) {
			if (new_freshness != this.freshness) {
				this.freshness = new_freshness;
				updateFreshnessInDOM(this.DOMID, new_freshness);
				updateNodeInSSC(this.DOMID);
			}
		}
		else {
			throw new Exception("Incorrect parameter: new_freshness.  A collapseNode's freshness must be either 'fresh,' 'stale,' or 'bell_air.'");
		}
	};
	
	that.getLastUpdate = function(){
		return this.last_update;
	};
	
	that.getCollapsedStatus = function(){
		return this.collapsed_status;
	};
	
	that.setCollapsedStatus = function(new_collapsed_status){
		//If this is a change...
		if (new_collapsed_status != this.collapsed_status) {
			if (new_collapsed_status == "collapsed") {
				this.collapsed_status = new_collapsed_status;
			}
			else 
				if (new_collapsed_status == "expanded") {
					this.collapsed_status = new_collapsed_status;
					//If I was fresh,
					if (this.freshness == "fresh") {
						//Look to see if I have any fresh or bell_air kids.  If so,
						if (this.bellAirLookDown()) {
							//My freshness becomes bell_air, and that's the end of it.  My ancestors don't need to know a thing.
							this.setFreshness("bell_air");
						}
						//But, if I have no freshness anywhere beneath me,
						else {
							//I become stale
							this.setFreshness("stale");
							//And I must bear the bad news to my parent.  If things are really rough, he'll tell his parent, and so on.
							if ((typeof(this.parentNode) != "string") && ("bellAirBubble" in this.parentNode)) {
								this.parentNode.bellAirBubble("negative");
							}
						}
					}
				}
				else {
					throw new Exception("Incorrect parameter: " + new_collapsed_status + ".  A collapseNode's collapsed_status must be either 'collapsed' or 'expanded.'");
				}
			//Finally, we issue an order for this node's info to be updated in the server-side Stored Status Collection
			updateNodeInSSC(this.DOMID);	
		}
	};
	
	//This function gets the DATETIME value indicating the most recent update to the entity corresponding to the collapseNode
	//This function is declared here as an abstract function because inheritor classes may use different names for this DATETIME value,
	//	e.g. "last_update" vs. "release_date" depending on the entity's degree of volatility.
	//I don't think we shall need this on the client-side presently (ND - 2008-03-18)
	//public abstract function getLastUpdate();
	
	//This function returns an array containing this object's attributes
	that.getSimpleArray = function(){
		//Return an object literal that acts like an associative array
		//Array must use these keys: parent_node, last_update, freshness, collapsed_status
		//First, get the best string available representing the parent node
		var parent_node = (typeof(this.parentNode) != "string" && "getMyDOMID" in this.parentNode) ? this.parentNode.getMyDOMID() : this.parentNode.toString();	 
		return {
			"parent_node": parent_node,
			"last_update": this.getLastUpdate(),
			"freshness": this.freshness,
			"collapsed_status": this.collapsed_status
		};
	};
	
	//The following functions are as of yet unnecessary (ND - 2008-03-18)	
	/*		
	 that.getCollapsedStatus = function() {
	 	return this.collapsed_status;
	 };
	 
	 */
	
	//Finally, return the object (formerly known as a treeNode) that we've been so busily augmenting.
	//It shall henceforth be known as "collapseNode"! [insert regal fanfare] 
	return that;
}
// <-- End "class" collapseNode


/*
//Define treeNode and collapseNode objects according to a "psuedoclassical" inheritance model
//The "inherits" method doesn't seem to have been properly added to the Function object prototype (ND - 2008-03-19)

//A node object in an unordered tree
//Definition and "Constructor":
function treeNode(DOMID, parent) {
	this.parentNode = parent;
	this.DOMID = DOMID;
	this.childNodes = new Array();
  }
	//"Methods":
	treeNode.prototype.getParent = function() {
		return this.parentNode;
	}
	treeNode.prototype.getChildren = function() {
		return this.childNodes;
	}
	treeNode.prototype.addChild = function() {
		$newChild = new treeNode(this);
		this.childNodes[this.childNodes.size] = $newChild;
	}
	treeNode.prototype.getMyDOMID = function() {
		return this.DOMID;
	}
		
	//	treeNode.prototype.deleteChild = function(childIndex) {
			//If index is valid, delete corresponding child
			//This involves traversing the subtree of which the child is the root, deleting each node as you return upwards, and finally deleting
			//	the child itself.  And by deletion, I mean removal from parent's child list, as well as unsetting/destruction of object.
	//	}
// <-- End "class" treeNode
	
	
//A "collapsable" node object that can also possess varying degrees of "freshness."
//Definition and "Constructor":
function collapseNode() {}
//Now we can declare that collapseNode "inherits" from treeNode (do this before defining any of collapseNode's public methods)
collapseNode.inherits(treeNode);
	//Now define the "constructor"
	collapseNode.prototype = function (DOMID, parent, last_update, freshness, collapsed_status) {
		this.last_update = last_update;
		this.freshness = freshness;
		this.collapsed_status = collapsed_status;
		//Pass the 'parent' parameter along to the the treeNode "class constructor"
		//TODO: I'm not 100% sure of the correct way to pass a value to the parent/super/uber class's constructor
		uber(DOMID, parent);
	  }

	//"Methods":
	
	//This function is called when a change has occurred involving the freshness of a node at a lower level in the tree
	collapseNode.prototype.bellAirBubble = function(positivity) {
		//"Positive" means that a node beneath us is newly fresh (it could be a prevously existing node or an entirely new one)
		if(positivity == "positive") {
			//If we were stale, the fresh node that is now somewhere beneath us requires us to be "bell_air" instead
			if(this.freshness == "stale") {
				this.freshness = "bell_air";
				//In this case, we need to pass the message on to our ancestor nodes in case they too are stale and need to update their
				//	freshness to reflect the newly fresh, instigator descendant node.
				if(this.parentNode) { //TODO: make sure this is really checking what I think it should be checking. why isn't null a reserved word?
					this.parentNode.bellAirBubble("positive");
				}
			}
		}
		//"Negative" means that a node beneath us is newly stale (it may prevously have been either "fresh" or "bell_air")
		else if(positivity == "negative") {
			//If we were "bell_air," the formerly fresh node somewhere beneath us (which is now stale or gone) requires us to check to see 
			//	whether we still have reason to be "bell_air."  
			//	I.e., was the instigator descendant node our only fresh descendant, or do we have others?
			if(this.freshness == "bell_air") {
				if(this.bellAirLookDown() == false) {
					this.freshness = "stale";
					//In this case, we need to pass the message on to our ancestor nodes in case they too are bell_air AND need to update 
					//	their freshness to reflect the staleness or disappearance of their last fresh descendant node.
					if(this.parentNode) { //TODO: make sure this is really checking what I think it should be checking. why isn't null a reserved word?
						this.parentNode.bellAirBubble("negative");
					}
				}
			}
		}
		else {
			throw new Exception("function 'bellAirBubble' was called with an invalid 'positivity' parameter: positivity.  This parameter must be either 'positive' or 'negative.'");
		}
		return;
	}
	
	//This function checks to see if we have any bell_air or fresh children
	//This is useful in checking to see whether we should keep our bell_air status
	collapseNode.prototype.bellAirLookDown = function() {
		for(child in this.childNodes) {
			if(child.getFreshness() == "fresh" || child.getFreshness() == "bell_air") {
				return true;
			}
		}
		return false;
	}
	
	collapseNode.prototype.getFreshness = function() {
		return this.freshness;
	}

	collapseNode.prototype.setFreshness = function(new_freshness) {
		if(new_freshness == "fresh" || new_freshness == "stale" || new_freshness == "bell_air") {
			this.freshness = new_freshness;
		}
		else throw new Exception("Incorrect parameter: new_freshness.  A collapseNode's freshness must be either 'fresh,' 'stale,' or 'bell_air.'");
	}

	collapseNode.prototype.getLastUpdate = function() {
		return this.last_update;
	}
	
	collapseNode.prototype.getCollapsedStatus = function() {
		return this.collapsed_status;
	}
	
	collapseNode.prototype.setCollapsedStatus = function(new_collapsed_status) {
		if(new_collapsed_status == "collapsed" || new_collapsed_status == "expanded") {
			this.collapsed_status = new_collapsed_status;
		}
		else throw new Exception("Incorrect parameter: new_collapsed_status.  A collapseNode's collapsed_status must be either 'collapsed' or 'expanded.'");
	}
	
	//This function gets the DATETIME value indicating the most recent update to the entity corresponding to the collapseNode
	//This function is declared here as an abstract function because inheritor classes may use different names for this DATETIME value,
	//	e.g. "last_update" vs. "release_date" depending on the entity's degree of volatility.
	//I don't think we shall need this on the client-side presently (ND - 2008-03-18)
	//public abstract function getLastUpdate();
	
	//This function returns an array containing this object's attributes
	collapseNode.prototype.getSimpleArray = function(){
		//Array must use these keys: parent_node, last_update, freshness, collapsed_status
		var simpleArray = {
			"parent_node": this.parentNode.getMyDOMID(),
			"last_update": this.getLastUpdate(),
			"freshness": this.freshness,
			"collapsed_status": this.collapsed_status
		};
		return simpleArray;
	}
	
	//The following functions are as of yet unnecessary (ND - 2008-03-18)	
	/*		
		collapseNode.prototype.getCollapsedStatus = function() {
			return this.collapsed_status;
		}
  <--remember to re-comment this last function if you un-comment the larger block	
*/
// <-- End "class" collapseNode


