//For debugging

/*
function debug(text) {
	if(window.console && console.log){
		console.log(text);
	} else if(window.opera && opera.postError){
		opera.postError(text);
	} else {
		alert(text);
	}
}
// */


/* =================================================

CaptivateController()
Version 0.9.1, works with Adobe Captivate 2, 3 and 4
by Philip Hutchison, May-July 2009
http://pipwerks.com/lab/captivate

Copyright (c) 2009 Philip Hutchison
MIT-style license. Full license text can be found at 
http://www.opensource.org/licenses/mit-license.php

==================================================== */

var CaptivateController = function (swfID, usesExternalSkin){

	//Shortcuts for compression improvement
	var label_skin = "cpSkinLoader_mc.";
	var prefix_rd = "rdcmnd";
	var prefix_cp = "cpCmnd";
	var prefix;
	var cpversion = null;
	var cpversionString = "";
	var isCaptivate = false;
	var skinPath = "";
	var getMethod = "GetVariable";
	var setMethod = "SetVariable";
	var undef = "undefined";
	var unknown = "unknown";
	var CaptivateVersion = "CaptivateVersion";
	var isAS3 = false;
    var gotoSlideUsesZeroIndex = false;
	var that = this;
	
	if(typeof swfID === undef){ return false; } //Error-checking is a good thing.
	
	var swf = document.getElementById(swfID); //Get SWF as an object so we can use SetVariable
	
	if(!swf){ return false; } //Error-checking is a good thing. 
	
	var init = function (){
		
		var version_cpGetValue = null;
		var version_cpGetValue_skinned = null;
		var version_getvariable = null;
		var version_getvariable_skinned = null;
		var cpGetValueSupported = false;
		var GetVariableSupported = false;
		
		/* --- Tests for versions: Captivate version & ActionScript version --- */
		
		//Test for GetVariable support and cpGetValue support. 
		//We can use this info to figure out if this is a Captivate 4 file (only CP4+ uses cpGetValue).	
		
		if(typeof swf.cpGetValue !== undef){
			try { version_cpGetValue = swf.cpGetValue(CaptivateVersion); } catch(e1) {/* do nothing */}
			try { version_cpGetValue_skinned = swf.cpGetValue(label_skin +CaptivateVersion); } catch(e2) {/* do nothing */}
		}
		
		if(typeof swf.GetVariable !== undef){
			try { version_getvariable = swf.GetVariable(CaptivateVersion); } catch(e3) {/* do nothing */}
			try { version_getvariable_skinned = swf.GetVariable(label_skin +CaptivateVersion);} catch(e4) {/* do nothing */}
		}
		
		//Using a simple typeof swf.GetVariable !== "undefined" won't work in Safari
		//Internet Explorer returns proprietary "unknown" as typeof when using ExternalInterface to retrieve variables (objects accessed over a COM+ bridge)
		GetVariableSupported = (typeof version_getvariable !== undef && typeof version_getvariable !== unknown && version_getvariable !== null) || 
							   (typeof version_getvariable_skinned !== undef && typeof version_getvariable_skinned !== unknown && version_getvariable_skinned !== null) || false;
		
		//Using a simple typeof swf.cpGetValue !== "undefined" won't work in Safari
		//Internet Explorer returns proprietary "unknown" as typeof when using ExternalInterface to retrieve variables (objects accessed over a COM+ bridge)
		cpGetValueSupported = ((typeof version_cpGetValue !== undef && typeof version_cpGetValue !== unknown && version_cpGetValue !== null) || 
							   (typeof version_cpGetValue_skinned !== undef && typeof version_cpGetValue_skinned !== unknown && version_cpGetValue_skinned !== null)) || false;

		//Get the string value of "CaptivateVersion".
		cpversionString = (cpGetValueSupported) ? version_cpGetValue || version_cpGetValue_skinned || false : version_getvariable || version_getvariable_skinned  || false;

		isCaptivate = (cpversionString !== false);
		
		if(!isCaptivate){ return false; } //This isn't a Captivate file.
		
		//Remove "v" prefix, if any
		cpversionString = cpversionString.replace(/v/gi, "");
		
		//We just want the major version, which comes before the first dot
		//Unfortunately there is no way to differentiate between a CP2 and CP3 file, since CP3 reports "2.0.0"
		cpversion = parseInt(cpversionString.split(".")[0], 10);
		
		//CP4+ AS3 files don't support GetVariable for retrieving variables
		isAS3 = (cpversion > 3 && !GetVariableSupported);
		
		/*
		debug("cpversionString: " +cpversionString +"\n" +
			"cpversion: " +cpversion +"\n" +
			"cpGetValueSupported: " +cpGetValueSupported +"\n" +
			"GetVariableSupported: " +GetVariableSupported +"\n" +
			"isAS3: " +isAS3 +"\n" +
			"isCaptivate: " +isCaptivate);
		*/
		
		
		/* Select appropriate get/set methods. Defaults are GetVariable and SetVariable.
		   Not using cpGetValue for AS2-based CP4+ files because it doesn't appear to work in all cases (no external skin).
		   It throws an error if encountering unknown parameter. AS3 version doesn't throw the error.
		   AS2 version returns same set of variables using GetVariable, and with no errors.
		   Only shortcoming is all data returned as string, not native data type.
		*/
		//if(cpGetValueSupported && isAS3){ 
		if(cpGetValueSupported){ 
			getMethod = "cpGetValue";
			setMethod = "cpSetValue";
		}

		//Test for skin
		if(!usesExternalSkin){ 
		
			var param = (cpversion > 3) ? "isCPMovie" : "rdIsMainMovie";			
			var externalSkinDetected = false;
			
			try {
				
				//Internet Explorer returns proprietary "unknown" as typeof when using ExternalInterface to retrieve variables (objects accessed over a COM+ bridge)
				externalSkinDetected = (typeof swf[getMethod](label_skin +param) !== undef && 
										typeof swf[getMethod](label_skin +param) !== unknown &&
										swf[getMethod](label_skin +param) !== null);
			} catch (e5){
				//do nothing
			}
			
			usesExternalSkin = externalSkinDetected;
			
		}
			
		skinPath = (usesExternalSkin) ? label_skin : "";
		prefix = (cpversion > 3) ? prefix_cp : prefix_rd;
		
	};
	
	var isCaptivateSWF = function (){
		
		//Ensure cpversion is populated
		if(cpversion === null){ init(); }
		
		//If cpversion isn't found, this isnt a Captivate SWF		
		return isCaptivate;
		
	};

	var control = function (command, num){
		
		if(!isCaptivateSWF()){ return false; }
        
        /*
            set value of num if num is undefined. 
            num is usually only defined when invoking 
            gotoSlideAndPlay, gotoSlideAndStop, gotoFrameAndPlay, 
            and gotoFrameAndStop
        */
        
		if(typeof num === undef){ num = 1; } 
		
		switch (command) {
		
			case "pause": command = prefix_rd + "Pause"; break;
			case "resume": command = prefix_rd + "Resume"; break;
			case "next": command = prefix_rd + "NextSlide"; break;
			case "previous": command = prefix_rd + "Previous"; break;
			case "rewindAndStop": command = prefix_rd + "RewindAndStop"; break;
			case "rewindAndPlay": command = prefix_rd + "RewindAndPlay"; break;
			case "gotoSlideAndPlay": 
            
                if(!gotoSlideUsesZeroIndex){ num = num -1; }
                
                //Go to slide
                swf[setMethod](skinPath + prefix + "GotoSlide", num); 
                
                //cpCmndGotoSlide will pause the movie, so let's resume playing.
                command = prefix_rd + "Resume";
                num = 1; 
                break;
			
            case "gotoSlideAndStop": 
            
                if(!gotoSlideUsesZeroIndex){ num = num -1; }
                command = prefix + "GotoSlide";
                break;                
                
			case "gotoFrameAndPlay": 
            
                //rdcmndGotoFrameAndResume only works when movie is paused, so let's pause first.
                swf[setMethod](skinPath + prefix_rd + "Pause", 1); 
                command = prefix_rd + "GotoFrameAndResume"; 
                break; 
			
			case "gotoFrameAndStop": command = prefix_rd + "GotoFrame"; break;
			case "volume": command = prefix_cp + "Volume"; break;
			case "mute": command = prefix + "Mute"; break;
			case "unmute": command = prefix + "Mute"; num = 0; break;
			
            case "muteAndShowCaptions": 
            
                //Mute first
                swf[setMethod](skinPath + prefix + "Mute", 1); 
                
                //Now show captions
                command = prefix + "CC"; 
                break;
			
            case "unmuteAndHideCaptions": 
            
                //Unmute first
                swf[setMethod](skinPath + prefix + "Mute", 0); 
                
                //Hide captions
                command = prefix + "CC"; num = 0; 
                break;
                
			case "showCaptions": command = prefix + "CC"; break;
			case "hideCaptions": command = prefix + "CC"; num = 0; break;
			case "info": command = prefix_rd + "Info"; break;
			case "hidePlaybar": command = (cpversion > 3) ? prefix_cp + "ShowPlaybar" : prefix_rd + "HidePlaybar"; num = (cpversion > 3) ? 0 : 1; break;
			case "showPlaybar": command = (cpversion > 3) ? prefix_cp + "ShowPlaybar" : prefix_rd + "HidePlaybar"; num = (cpversion > 3) ? 1 : 0; break;
			case "lockTOC": command = (cpversion > 3) ? "cpLockTOC" : false; break; //Enables/disables user interaction on TOC. Only works in CP4+.
			case "unlockTOC": command = (cpversion > 3) ? "cpLockTOC" : false; num = 0; break; //Enables/disables user interaction on TOC. Only works in CP4+.
			case "exit": command = prefix_rd + "Exit"; break;		

			default: command = false; //Explicitly setting fall-through command to false so we can test for validity below
		
		}
		
		if(command){ swf[setMethod](skinPath + command, num); }
		
		return that; //returning 'that' allows chaining
	
	};
	
	var getInfo = function (param){	
	
		if(!isCaptivateSWF()){ return false; }

		//debug("querying " +param);

		var result = null;
		
		switch(param){
			
			//"rdinfoHasPlaybar" is handled differently because it targets the skin file
			case "rdinfoHasPlaybar":
			
				try { result = swf[getMethod](param); } catch(e1){ /* do nothing */ }
				if(typeof result === undef || result === null){
					try { result = swf[getMethod](skinPath + param); } catch(e2){ /* do nothing */ }				
				}
				result = (typeof result !== undef) ? result : false;
				break;
			
			//"playbarHeight" and "playbarPosition" are available in the skin in AS2 files.
			case "playbarHeight":
			case "playbarPosition":
			
				if(!isAS3){
					
					try { 
						result = swf.GetVariable(param); 
					} catch(e3){ 
						try { result = swf.GetVariable("cpSkinLoader_mc." + param); } catch(e4){ /* do nothing */ }
					}
					
				} else {
					result = swf[getMethod](skinPath + param);
				}
				break;
				
			default: try { result = swf[getMethod](skinPath + param); } catch(e5){ /* do nothing */ }	
		
		}
		
		//Ensure all "undefined" return as null
		return (typeof result !== undef) ? result : null;
	
	};

	// --- API for controlling Captivate SWF ---
	this.pause = function (){ return control("pause"); };
	this.resume = function (){ return control("resume"); };
	this.next = function (){ return control("next"); };
	this.previous = function (){ return control("previous"); };
	this.rewindAndStop = function (){ return control("rewindAndStop"); };
	this.rewindAndPlay = function (){ return control("rewindAndPlay"); };
	this.gotoSlideAndStop = function (num){ if(typeof num === "number"){ return control("gotoSlideAndStop", num); } };
	this.gotoSlideAndPlay = function (num){ if(typeof num === "number"){ return control("gotoSlideAndPlay", num); } };
	this.gotoFrameAndStop = function (num){ if(typeof num === "number"){ return control("gotoFrameAndStop", num); } };
	this.gotoFrameAndPlay = function (num){ if(typeof num === "number"){ return control("gotoFrameAndPlay", num); } };
	this.showInfoBox = function (){ return control("info"); };
	this.exit = function (){ return control("exit"); };
	this.lockTOC = function (){ return control("lockTOC"); };
	this.unlockTOC = function (){ return control("unlockTOC"); };
	this.hidePlaybar = function (){ return control("hidePlaybar"); };
	this.showPlaybar = function (){ return control("showPlaybar"); };
	this.mute = function (){ return control("mute"); };
	this.unmute = function (){ return control("unmute"); };
	this.muteAndShowCaptions = function (){ return control("muteAndShowCaptions"); };
	this.unmuteAndHideCaptions = function (){ return control("unmuteAndHideCaptions"); };
	this.showCaptions = function (){ return control("showCaptions"); };
	this.hideCaptions = function (){ return control("hideCaptions"); };
	
	this.volume = function(num){ 
	
		if(isCaptivateSWF() && (cpversion > 3)){
			//Set volume
			if(typeof num === "number"){ control("volume", num); }
			//return volume level
			return getInfo("cpCmndVolume");
		}
		
		//Setting/getting volume only works in CP4+.
		return null;
	
	};
	
    // -- Set options -- 
    this.useZeroIndex = function(bool){ gotoSlideUsesZeroIndex = (bool) ? true : false; };
    
	// --- Shortcuts for getting common info ---
	this.captivateVersion = function (){ return (isCaptivateSWF()) ? cpversion : false; };
	this.asVersion = function (){ return (isCaptivateSWF()) ? (isAS3) ? 3 : 2 : false; };
	this.FPS = function (){
		if(!isCaptivateSWF()){ return false; }
		return getInfo("rdinfoFPS") || getInfo("cpInfoFPS") || "";
	};
	this.hasSkinSWF = function (){ return (isCaptivateSWF()) ? usesExternalSkin : false; };
	this.hasTOC = function (){ return (isCaptivateSWF() && getInfo("NoOfTOCEntries") !== null) ? true : false; };
	
	//rdinfoHasPlaybar seems to return true even if no playbar is present. 
	//Wonder if it's triggered by publishing a skinned SWF then not using the skin.
	
	this.hasPlaybar = function (){
		if(!isCaptivateSWF()){ return false; }
		//Purposely using == instead of === for 'truthiness' not strict equality
		if(cpversion > 3){ return (getInfo("cpInfoHasPlaybar") == 1) ? true : false; }
		return (getInfo("rdinfoHasPlaybar") == 1) ? true : false;
	};
	
	this.width = function (){ return (isCaptivateSWF() && (cpversion > 3)) ? getInfo("cpMovieWidth") : swf.TGetProperty("/", 8); };
	this.height = function (){ return (isCaptivateSWF() && (cpversion > 3)) ? getInfo("cpMovieHeight") : swf.TGetProperty("/", 9); };
	
	/* --- Flash (not Captivate) properties --- */
	this.percentLoaded = function (){ return swf.PercentLoaded(); };
	this.getname = function (){ return swf.TGetProperty("/", 13); };
	this.geturl = function (){ return swf.TGetProperty("/", 15); };
	
	//API for querying Captivate SWF
	this.query = function (param){ return getInfo(param); };
	this.swf = swf;
		
	return this;

};