/**
 * C3DObject embed
 *
 * Based on
 * Geoff Stearns (geoff@deconcept.com, http://www.deconcept.com/)
 * http://blog.deconcept.com/2005/01/26/web-standards-compliant-javascript-quicktime-detect-and-embed/
 * Licensed under MIT license. http://www.opensource.org/licenses/mit-license.php
 *
 * Embeds a Cult3D object to the HTML page
 *
 * Usage:
 *
 *  myC3DObject = new C3DObject("path/to/cult3d.co", "objectID", "width", "height");
 *  myC3DObject.addAntialiasing(myC3DObject.AA_AUTO); // optional, convenience functions exists for Cult3D embedd parameters
 *  myC3DObject.write();
 *
 *
 */

/**
 * Jonatan Andersson, 2006-04-25
 * Last edited: 2007-02-20
 * version 1.031
 */


 /*
 ----------------------------------------------------------------
 List of functions that can be binded to the Cult3D instance


 --- Constructor ---

  C3DObject("<Filename.co>", "<instanceName>", "<width>", "<height>");


 --- Functions that alters the Cult3D player ---

  addAntialiasing(<antialiasing mode (AA_AUTOMATIC, AA_AUTO, AA_OFF, AA_FORCED)>);
  setAntialiasingDelay(<delay in milliseconds>);

  setProgressBarColor(<a hex color>);
  disableProgressbar();

  disableHW(<HW_DISABLED, HW_ENABLED>);
  viewFinished(<VIEW_FINISHED_RENDER, VIEW_FINISHED_HOLD>);
  frameSkip(<FRAME_SKIP_DISABLE, FRAME_SKIP_ENABLE>);


 --- Cult3D Events ----

  onSendMessage("<function callback name>");
  onSceneLoaded("<function callback name>");
  onLoadFinished("<function callback name>");
  onLoadAborted("<function callback name>");





----------------------------------------------
*/



// --- Constructor ---


/**
 * Constructor to create a new  Cult3D embedd string
 * myC3DObject = new C3DObject("path/to/cult3d.co", "CultObject", "400", "400");
 */
C3DObject = function(object, id, width, height)
{
    this.object = object;
    this.id = id;
    this.width = width;
    this.height = height;
    this.params = new Object();
}



// --- Constants -----

/**
 * Antialiasing constants
 *
 * These parameters controls the software antialiasing (doesn't affect hardware rendering).
 *
 * It can be set to three different values:
 * AA_AUTOMATIC – automatic antialiasing, if antialiasing is turned on when nothing has happened in the scene after the time period set by “ANTIALIASINGDELAY”. When anything moves, antialiasing is turned off to improve speed. This is the default value.
 * AA_OFF       – disable antialiasing
 * AA_FORCED    – enable antialiasing
 */
C3DObject.prototype.AA_AUTOMATIC = 0;           // Let antialiasing kick in after a delay. Default
C3DObject.prototype.AA_OFF = 1;                 // Turn off antialiasing
C3DObject.prototype.AA_FORCED = 2;              // Force antialiasing to be always on
C3DObject.prototype.AA_AUTO= this.AA_AUTOMATIC; // An alias for automatic antialiasing


/**
 * Hardware enabled/disabled constants
 *
 * These parameters controls hardware acceleration.
 *
 * Default value is HW_ENABLED which enables hardware acceleration if any supported graphics card is installed in the client machine.
 * Set the value to HW_DISABLED to disables all hardware acceleration.
 */
C3DObject.prototype.HW_ENABLED  = 0; // Enable hardware support. Default if not eplicitly disabled
C3DObject.prototype.HW_DISABLED = 1; // Disable hardware support.


/**
 * Frame skip enabled/disabled constants
 *
 * These parameters controls the flow of the animation
 *
 * By default if the Cult3D scene is large and the computer has problem keeping up the Cult3D player skips some steps in the animation.
 * If it is important for you to see the full animation you can force Cult3D to play the full animation.
 *
 * Default value is FRAME_SKIP_DISABLE
 *
 */

C3DObject.prototype.FRAME_SKIP_DISABLE = 0; // May skip frames on slower computers. Default
C3DObject.prototype.FRAME_SKIP_ENABLE  = 1; // Plays the full animation.

C3DObject.prototype.VIEW_FINISHED_RENDER = 0;
C3DObject.prototype.VIEW_FINISHED_HOLD   = 1;


// Event variables for Cult3D
C3DObject.prototype.OnLoadFinishedEvent = "OnLoadFinished";
C3DObject.prototype.OnSendMessageEvent  = "OnSendMessage";
C3DObject.prototype.OnSceneLoadedEvent  = "OnSceneLoaded";
C3DObject.prototype.OnLoadAbortedEvent  = "OnLoadAborted";



// !!!! CHECKME: Update this when a newer Cult3D version is released
C3DObject.prototype.MS_IE_C3DPLUGIN_VERSION = "5,3,0,228";
C3DObject.prototype.MS_IE_PLUGIN_DOWNLOAD = "http://www.cult3d.com/download/cult.cab";
C3DObject.prototype.NETSCAPE_PLUGIN_DOWNLOAD = "http://www.cult3d.com/newuser/index.html";



/**
 * Write the Cult3D embedding tag in the website
 *
 * @elementID - The name of a layer elementID which embedd tag will replace,
 *              if the value is null it writes at the location of the execution.
 */
C3DObject.prototype.write = function(elementId)
{
    if (elementId)
    {
        document.getElementById(elementId).innerHTML = this.getHTML();
    }
    else
    {
        document.write(this.getHTML());
    }
}


///////////////////////////////////
// Cult3D convenience functions //
///////////////////////////////////

/**
 * Adds antialising parameter to the embedded object
 * Use the defined AA_OFF, AA_AUTOMATIC, AA_FORCED to set the degree of antialiasing you want
 */
C3DObject.prototype.addAntialiasing = function(aaState)
{
	if ((aaState < 0) || (aaState > 2))
	{
		alert("[includeCult3.js setAntialiasing] - Unknown antialiasing state " + aaState);
		return;
	}

	this.addParam("Antialiasing", aaState);
}

/**
 * How long should the scene stand still before the antialiasing kicks in.
 * The delay is in milliseconds. Default value is 1000 milliseconds (1 second).
 * Its only usable if antialising is in automatic mode.
 */
C3DObject.prototype.setAntialiasingDelay = function(aaDelay)
{
	if (aaDelay < 0)
	{
		alert("[includeCult3D.js addAntialiasingDelay] - Antialiasing delay needs to be positive");
		return;
	}

	this.addParam("AntialiasingDelay", aaDelay);
}

/**
 * Cult3D will trigger this event whenever it receives a
 * "Send Message to Host" call activated by the Cul3D
 * Designer or by a Cult3D Java method.
 *
 * Retrieve the message by calling the method "getLastMessage()", see Cult3D.js for that function specification.
 */
C3DObject.prototype.onSendMessage = function(functionName)
{
	if ((functionName == null) || (functionName == ""))
	{
		alert("[includeCult3D.js onSendMessage] - The parameter functionName is empty");
		return;
	}

	functionName = this.validateFunctionName(functionName);
	this.addParam(this.OnSendMessageEvent, functionName);
}

/**
 * Cult3D will trigger this event when enough information has been loaded to
 * start the scene. Textures and cursors might not have been loaded yet
 */
C3DObject.prototype.onSceneLoaded = function(functionName)
{
	if ((functionName == null) || (functionName == ""))
	{
		alert("[includeCult3D.js onSceneLoaded] - The parameter functionName is empty");
		return;
	}

	functionName = this.validateFunctionName(functionName);
	this.addParam(this.OnSceneLoadedEvent, functionName);
}

/**
 * Cult3D will trigger this event when the full Cult3D file has been loaded and decompressed.
 */
C3DObject.prototype.onLoadFinished = function(functionName)
{
	if ((functionName == null) || (functionName == ""))
	{
		alert("[includeCult3D.js onLoadFinished()] - The parameter functionName is empty");
		return;
	}

	if (this.isIE())
	{
		functionName = this.validateFunctionName(functionName, "this");
	}
	else
	{
		functionName = this.validateFunctionName(functionName, "'" + this.id + "'");
	}

	this.addParam(this.OnLoadFinishedEvent, functionName);
}

/**
 * Cult3D will trigger this event when the Cult3D file is either invalid or
 * can't be found on the path specified in the includeCult3D.js constructor.
 */
C3DObject.prototype.onLoadAborted = function(functionName)
{
	if ((functionName == null) || (functionName == ""))
	{
		alert("[includeCult3D.js onLoadAborted] - The parameter functionName is empty");
		return;
	}

	functionName = this.validateFunctionName(functionName);
	this.addParam(this.OnLoadAbortedEvent, functionName);
}

/**
 * Set param hardwareSupport to HW_DISABLED to disable hardware support for the Cult3D Scene.
 */
C3DObject.prototype.disableHW = function(hardwareSupport)
{
	if ((hardwareSupport != this.HW_DISABLED) || (hardwareSupport != this.HW_ENABLED))
	{
		alert("[includeCult3D.js disableHW] - The parameter hardwareSupport is invalid " + hardwareSupport);
		return;
	}

	this.addParam("DisableHW", hardwareSupport);
}



/**
 * This parameter controls automatic frame skipping to compensate for slower computers.
 * Default value is FRAME_SKIP_ENABLE which enables frame skipping. Set the value to
 * FRAME_SKIP_DISABLE to force Cult3D to animate every frame.
*/
C3DObject.prototype.frameSkip = function(frameState)
{
	if ((frameState != FRAME_SKIP_DISABLE) || (frameState != FRAME_SKIP_ENABLE))
	{
		alert("[includeCult3D.js frameSkip] - The parameter frameState is invalid " + frameState);
		return;
	}

	this.addParam("FrameSkip", frameState);
}



/**
 * This parameter controls the rendering of the Cult3D object while the
 * object is loading.
 *
 * Default value is "VIEW_FINISHED_RENDER" which allows Cult3D to render
 * the object while it is loading the object.
 * set to VIEW_FINISHED_HOLD to force Cult3D to not render anything until
 * the object is completely loaded.
 */
C3DObject.prototype.viewFinished = function(renderState)
{
	if ((renderState != VIEW_FINISHED_RENDER) || (renderState != VIEW_FINISHED_HOLD))
	{
		alert("[includeCult3D.js viewFinished] - The parameter renderState is invalid " + renderState);
		return;
	}

	this.addParam("ViewFinished", frameState);
}

/**
 * This disables the Cult3D progress bar when loading the Cult3D object.
 */
C3DObject.prototype.disableProgressbar = function()
{
    this.addParam("DisablePB", "1");
}


/**
 * This sets the color of the Cult3D progress bar while the Cult3D object is loading.
 * If no value is given or the value is invalid, it will automatically
 * be set to white. The color must be a valid browser hex value.
 */
C3DObject.prototype.setProgressBarColor = function(hexColor)
{
    if (! this.isValidHexColor(hexColor))
    {
        alert("[includeCult3D.js progressBarColor] - The progress bar color is not a valid hex number");
        return;
    }

    var c3dHex = hexColor.substring(1, hexColor.length); // Remove the # from the string
    this.addParam("PBColor", c3dHex);
}



////////////////////////////
// Embedd functions       //
////////////////////////////


C3DObject.prototype.addParam = function(name, value)
{
    this.params[name] = value;
}

C3DObject.prototype.getParams = function()
{
    return this.params;
}

C3DObject.prototype.getParam = function(name)
{
    return this.params[name];
}

C3DObject.prototype.getHTML = function()
{
    var c3dHTML = "";
    var eventHTML = ""; // For Explorer

    // not ie
	if (! this.isIE())
    {
        c3dHTML += '<embed type=\"application/x-cult3d-object\"\n';
        c3dHTML += '   id="' + this.id + '"\n';
        c3dHTML += '   pluginspage="' + this.NETSCAPE_PLUGIN_DOWNLOAD + '"\n';
        c3dHTML += '   src="' + this.object + '"\n';
        c3dHTML += '   width="' + this.width + '" height="' + this.height + '"\n';

        for (var param in this.getParams()) {
            c3dHTML += '   ' + param + '="' + this.getParam(param) + '"\n';
        }
        c3dHTML += "></embed>\n";
    }
    else // pc ie
    {
	    c3dHTML += '<object classid="clsid:31B7EB4E-8B4B-11D1-A789-00A0CC6651A8"\n';
   	    c3dHTML += '   id="' + this.id + '"\n';
  	    c3dHTML += '   codebase="' +  this.MS_IE_PLUGIN_DOWNLOAD + '#version=' + this.MS_IE_C3DPLUGIN_VERSION + '"\n';
	    c3dHTML += '   width="' + this.width + '" height="' + this.height + '"\n';
		c3dHTML += '>\n';
		c3dHTML += '   <param name="src" value="' + this.object + '">\n';
	    for (var param in this.getParams())
    	{
			// Disregard these events from param list and add them as separate javascript event blocks.
			if(param.search(this.OnSendMessageEvent) > -1)
			{
				var callback = this.getParam(this.OnSendMessageEvent);
				eventHTML += this.getExplorerEvents(this.OnSendMessageEvent, callback) + "\n";
				continue;
			}
			else if(param.indexOf(this.OnLoadFinishedEvent) > -1)
			{
				var callback = this.getParam(this.OnLoadFinishedEvent);
				eventHTML += this.getExplorerEvents(this.OnLoadFinishedEvent, callback) + "\n";
				continue;
			}
			else if	(param.search(this.OnSceneLoadedEvent) > -1)
			{
				var callback = this.getParam(this.OnSceneLoadedEvent);
				eventHTML += this.getExplorerEvents(this.OnSceneLoadedEvent, callback) + "\n";
				continue;
			}
			else if (param.search(this.OnLoadAbortedEvent) > -1)
			{
				var callback = this.getParam(this.OnLoadAbortedEvent);
				eventHTML += this.getExplorerEvents(this.OnLoadAbortedEvent, callback) + "\n";
				continue;
			}

			c3dHTML += '   <param name="' + param + '" value="' + this.getParam(param) + '">\n';
		}
        c3dHTML += '</object>';
    }

    return eventHTML + "\n\n" + c3dHTML;
}


C3DObject.prototype.getVariablePairs = function()
{
    var variablePairs = new Array();
    for (var name in this.getVariables())
    {
        variablePairs.push(name + "=" + escape(this.getVariable(name)));
    }

    if (variablePairs.length > 0)
    {
        return variablePairs.join("&");
    }
    else
    {
        return null;
    }
}

/////////////////////////////
// Private util functions //
///////////////////////////

/**
 * Special function for defining Internet Explorer Cult3D event callbacks
 *
 * @event - One of Cult3D events
 * @functionName - The function which will be called when 'event' is triggered
 */
C3DObject.prototype.getExplorerEvents = function(event, functionName)
{
	var c3dHTML = "";
	c3dHTML += '<SCRIPT LANGUAGE="javascript" FOR=' + this.id + ' EVENT=' + event + '>\n';
	c3dHTML += '<!--\n';
	c3dHTML += 'if (isActiveX())\n';
	c3dHTML += '{\n';
	c3dHTML += '  ' + functionName + '\n';
	c3dHTML += '}\n';
	c3dHTML += '// -->\n';
	c3dHTML += '</SCRIPT>\n';

	return c3dHTML;
}

// Adds paranthesis around the function name if none exists
// Inserts the parameters inside the function name
C3DObject.prototype.validateFunctionName = function(functionName, params)
{
	if (params == null)
	{
		params = "";
	}

	if ((functionName.indexOf("(") == -1) && (functionName.indexOf(")") == -1))
	{
		functionName += "(" + params + ")";
	}
	functionName += ";";
	return functionName;
}


C3DObject.prototype.isIE = function()
{
    if (navigator.plugins && navigator.plugins.length)
    {
    	return false;
    }

    return true;
}

C3DObject.prototype.isValidHexColor = function(hexColor)
{
    var reg = new RegExp('^#[0-9a-zA-Z]{6}$', 'g');
    return Boolean(reg.exec(hexColor));

}

