/**
 * This file contains all methods that manipulate, listen and detect changes in the hash string of the browser's URL. 
 */

function Hash()
{
	this.myReflexes = new Array();
	this.myCurrent = "";
}


/**
 * Programs a new reflex.
 */
Hash.prototype.addReflex = function ( aHashKey, aFunction, aContext )
{

	var foundIt = false;

	for (var i in this.myReflexes )
	{

		if (this.myReflexes[i].getKey() == aHashKey)
		{
			this.myReflexes[i] = new HashReflex ( aHashKey, aFunction, aContext );
			foundIt = true;
		}

	}

	if (!foundIt)
	{
		this.myReflexes.push( new HashReflex ( aHashKey, aFunction, aContext ) );
	}
}


Hash.prototype.executeReflex = function ( aHashKey, anArgument )
{
	for ( var i in this.myReflexes )
	{
		if (this.myReflexes[i].getKey() == aHashKey)
		{
			this.myReflexes[i].execute( anArgument );
		}
	}
}


/**
 * Returns everything past a special character in the string, usually the hash "#" symbol but if a question
 * mark exists, it will return everything past that. If neither symbol is found, it returns the entire string.
 */
Hash.prototype.get = function()
{

	var delimiter;
	var variables;

	variables = unescape( new String ( window.location ) );

	delimiter = variables.indexOf("#") == -1 ? "?" : "#";

	return variables.substring( variables.indexOf(delimiter) + 1, variables.length );

} // END of getHash()


/** Updates/inserts/deletes a key/value pair in the window's hash string.
  * If the variable name is found, it's value is updated. If it's not found, it's inserted. 
  * If aValue is null the variable is removed from the hash string.
  * @param aVariableName name of the key to insert/update/delete.
  * @param aValue value to associate with the variable name, null deletes the variable from the string.
  * @return the updated hash string 
  */
Hash.prototype.update = function ( aKey, aValue )
{

	var myResult;
	var allPairs, onePair, oneKey, oneValue;
	var didUpdate;
	var aKey;

	var locationWithoutVars;

	var isValidVariable;

	var allPairs = new Array();


	myResult 	= "#";

	rawPairs 	= this.get().split( "&" ); 

	didUpdate 	= false;

	aKey		= aKey.removePound(); // ensure our data

	// clean the data
	for ( var i in rawPairs )
	{

		isValidVariable = false;

		for ( var j in this.myReflexes )
		{
			oneReflex = this.myReflexes[j];
			if (rawPairs[i].split("=")[0] == oneReflex.getKey())
			{
				isValidVariable = true;	
			} 
		}

		if ( isValidVariable &&  rawPairs[i].length != 0 && rawPairs[i].indexOf("=null") == -1 )
		{

			allPairs.push(rawPairs[i]);

		}

	}

	// Go through each pair in the hash
	for ( var i in allPairs )
	{

		// easy handles
		onePair 	= allPairs[i].split("=");
		oneKey		= onePair[0];
		oneValue 	= onePair[1];

			// look for the one to update if we're still looking
			if ( didUpdate == false && oneKey == aKey )
			{
				oneValue = aValue;
				myResult += !(aValue == "null" || aValue == null) ? oneKey + "=" + oneValue + "&" : ""; // last & will be truncated
				didUpdate = true;
			} else
			{ // If it's not the one we're looking for or we're done updating, just add it and keep going.
				myResult += allPairs[i] + "&"; // truncate last &
			}		

	}

	// If we didn't find it, we couldn't update it. Add it. 
	myResult += (!didUpdate && !(aValue == "null" || aValue == null)) ? aKey + "=" + aValue + "&" : ""; //  last & will be truncated	
	
	
	if (myResult.length == 1)
	{
		myResult += " "; // extra space so we can just cut out one char no matter what.
	}
	
	// truncate last &
	myResult = myResult.substr(0, myResult.length - 1 ); 
	
	myURL = new String ( window.location );
	
	// make it so
	if (myURL.indexOf("?") != -1)
	{
		locationWithoutVariables = myURL.substr(0, myURL.indexOf("?"));
		window.location = locationWithoutVariables + myResult;
	} else
	{
		window.location.hash = myResult;
	}
	return false;

} // END of update()


/**
 * Calls handleHashChange() when a change is detected.
 */
Hash.prototype.check = function()
{

    if ( this.get() != this.myCurrent )
	{
		this.myCurrent = this.get();
		this.handleChange( this.myCurrent );
    }

} // END of check()


/**
 * Updates "current" hash record, and each individual reflex's record, effectively surpressing a change detection.
 */
Hash.prototype.supressDetection = function ()
{

	this.myCurrent = this.get();

	for ( var i in this.myReflexes )
	{

		// handle for one reflex
		oneReflex = this.myReflexes[i];

		// 
		oneCurrent = this.get().getValue( oneReflex.getKey() );

		oneReflex.setCurrent( oneCurrent );
		oneReflex.setOld( oneCurrent );

	}

}

/**
 * Checks to see if a hash variable has changed
 */
Hash.prototype.isChanged = function( aVariable )
{


	var oldValue;
	var newValue;
	var oneReflex, oneKey;

	for ( var i in this.myReflexes )
	{

		oneReflex = this.myReflexes[i];
		oneKey = oneReflex.getKey();

		if ( oneKey == aVariable )
		{

			oldValue = oneReflex.getCurrent();
			newValue = this.get().getValue(aVariable);

			if ( oldValue != newValue )
			{
				return true;
			}

		}

	}
	
	return false;

} // END of isChanged


/** 
 * Called by checkAddress() when the address changes and decides what functions to call based on the hash variables.
 */
Hash.prototype.handleChange = function()
{

	var newHash;
	var allPairs;
	var loadedVideo = false;
	var oneReflex, oneCurrent, oneOld, oneKey;

	var oldValue, newValue;

	allPairs 	= this.get().split("&");

	newHash 	= new String( this.get() );

	// iterate through each registered value. See "Registered Hash Keys"
	for ( var i in this.myReflexes )
	{

		oneReflex = this.myReflexes[i];

		// Detect which variables changed
		oldCurrent = oneReflex.getCurrent();

		oneReflex.setOld( oldCurrent );

		realCurrent = newHash.getValue( oneReflex.getKey() );

		oneReflex.setCurrent( realCurrent ); // get value if its there

		oneCurrent 	= oneReflex.getCurrent();
		oneOld		= oneReflex.getOld();
		oneKey		= oneReflex.getKey();

		// If there's a change
		if ( oneReflex.getCurrent() !=  oneReflex.getOld() )
		{
			// if it's not empty
			if (!!oneCurrent)
			{

				// call designated handler
				oneReflex.execute();
				
			}

			/*
			 * Big glaring exceptions to our pretty rules
			 */

			// If there is no show page specified
			if ( oneKey == "s" && !oneCurrent )
			{

				// See if there's a video specified
				if ( newHash.getValue("v") )
				{ // if so, find out what show it belongs to and load that page

					// Play the video
					playVideo( newHash.getValue("v") );

					// add the show to the hash
					var recognizedShow = getVideoById(newHash.getValue("v")).getShowKey();

					this.update("s", recognizedShow);

					/*
					 * surpress a change detection
					 */

					this.supressDetection();

					oneCurrent 	= oneReflex.getCurrent();
					oneOld		= oneReflex.getOld();
					oneKey		= oneReflex.getKey();

					// execute show handling reflex
					this.executeReflex("s", recognizedShow);

				//videoMenuObject.updatePlayerTitleDescription();
				} else
				{
					// If theres no video specified and no page specified, load the homepage
					loadPage(homePage);
				}

			} // END of hashKeys[i] == "s" && !currentHashValues[i]

			// If the show page has changed, clear the other variables.
			if ( oneKey == "s" && oneOld != oneCurrent)
			{

				for ( var j in this.myReflexes )
				{
					if ( this.myReflexes[j].getKey() != "s" ) 
					{
						this.update ( this.myReflexes[j].getKey(), "null" );
					}
					
				}
			
			} // hashKeys[i] == "s"
			
			if ( oneKey == "hp" && oneCurrent == null && !upperDrawerIsClosed)
			{
				toggleUpperDrawer();
			}
		
		} // currentHashValues[i] != oldHashValues[i] 
	
	} // var i in hashKeys 
	
} // END of handleHashChange
