/*
  Title: javascript api
*/

/* common global variables and functions */
if (window.Element) {Element.prototype.sendCmdToFlash = function sendCmdToFlash(cmd, params) { _vidi.throwerror("Flash communication failed"); }; }
// This function is required to prevent throw api user errors to flash
// if marshallExceptions is enabled.
_vidi = {
	conf: {
		debug: true,
		initialized: false,
		usercallback: function(){},
		camera: true,
		mic: true,
		screen: true,
		speaker: true,
		mic_volume:100,
		speaker_volume:100
	},
	client_debug_buffer: [],
	taggedprintf: function taggedprintf(tag, args) {
		// if args is an array convert it to object to adapt logs sent by flash.
		var args_object = {}; // new Object();
		if (args.length) {
			for (var j = 0; j < args.length; j++) {
				args_object[j] =  args[j];
			}
		} else {
			args_object = args;
		}
		var args_array=[];
		for (var i in args_object) {
			if (true) { // hack to prevent jslint warnings
				// Because of sprintf like parameters of console.log we cannot append a string parameter as first parameter
				if (i==='0' || i===0) {
					if (typeof args_object[i] == "string") {
						args_object[i] = tag + ": " + args_object[i];
					} else {
						args_array.push(tag + ": ");
					}
				}
				if (typeof args_object[i] == "function") { // print function name only
					args_object[i] = args_object[i].toString().split('\n',1)[0];
				}
				args_array.push(args_object[i]);
			}
		}
		if (_vidi.conf.debug && window.console) {
			console.log.apply(window.console, args_array);
		}
		// TODO: rewrite args_array into something legible by server. that is:
		// the %o should be replaced by variables if possible.
		_vidi.logtoserver(args_array);
	},

	logtoserver: function clientlog(log) {
		// FIXME: only works on firefox
		_vidi.client_debug_buffer.push(log.toString());
		if (_vidi.instances.masterscreen && _vidi.io.is_connected) {
			for (var line in _vidi.client_debug_buffer) {
				// console.log("sending line: %o", _vidi.client_debug_buffer[line]);
				if (_vidi.instances.masterscreen) {
					_vidi.io.sendCmdToFlash(_vidi.instances.masterscreen.swfid, "sendClientLogToServer", {log: _vidi.client_debug_buffer[line]});
				}
			}
			_vidi.client_debug_buffer = []; // reset buffer
		} else {
			// console.log("buffered line ",log.toString());
		}
	},

	flashlog: function flashlog(args_object) {
		_vidi.taggedprintf("FLASH", args_object);
	},

	apilog: function apilog() {
		_vidi.taggedprintf("VIDIJS", arguments);
	},

	debuglog: function debuglog() {
		_vidi.taggedprintf("VIDIJS: DEBUG:", arguments);
	},

	// Javascript Error object information for throwing exceptions:
	// {general} Properties: [message, name]
	// Firefox: # https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error
	// Firefox: Constructor: "throw new Error([message[, fileName[, lineNumber]]])"
	// Firefox: Properties: [{general}, message, fileName, lineNumber, stack]
	// IE: # http://msdn.microsoft.com/en-us/library/t9zk6eay.aspx
	// IE: Constructor: "throw new Error([number, [description]])"
	// IE: Properties: [{general}, description, message, name, number]
	throwerror: function throwerror(message) {
		var error;
		if (navigator.appName == "Microsoft Internet Explorer") {
			error = new Error(1, message);
		} else {
			error = new Error(message);
		}
		// Logging error object make stack available from firebug,
		// Also server will be aware about exception, with logging.
		_vidi.apilog("Throwing error with message[%o], error[%o]", message, error);
		throw error;
	},

	calllater: function calllater(fn) {
		var delay = 10; // in milliseconds
		return function() {
			var args = arguments;
			setTimeout(function(){fn.apply(fn, args);}, delay);
		};
	}
};

_vidi.instances = {
	properties: { "*": { "*": { "*": {} } } },
	screens: {},
	masterscreen: undefined
};

// Hack for firebug console.log.  Loading any frame after page is
// opened prevents use of console log.	Use parent's console.log if it
// exists.
// http://groups.google.com/group/firebug/browse_thread/thread/206b157f88ea7424?pli=1
// if (parent && parent.console) {
// 	(console.log = parent.console.log);
// }


/* global functions for flash communication */
_vidi.io = {
	is_connected: false,
	flash_loaded: function flash_loaded(swfid) {
		_vidi.apilog('"flash loaded" fired from Flash[%o], calling user callback', swfid);
		if (_vidi.instances.screens[swfid] === undefined) {
			_vidi.throwerror("screen with swfid[" + swfid + "] does not exist!");
		}
		_vidi.conf.usercallback("flash_loaded", {
				divid: swfid
			});
	},
	//TODO: sendCmdToFlash should be private in screen instances (ex: screen, audio_chat or local_echo)
	sendCmdToFlash: function sendCmdToFlash(swfid, cmdname, cmdargs) {
		//_vidi.apilog("js calling %o(%o) command for flash[%o]", cmdname, cmdargs || "WITH_NOPARAM", swfid);
		return document.getElementById(swfid).sendCmdToFlash(cmdname, cmdargs);
	},
	callback: function callback(msg) {
		var cmd = msg;
		_vidi.apilog("received vidi cmd-%o with message-%o", cmd, msg);
		switch (cmd.name) {
		case "received_textchat":
			//nothing to do
			var textmsg = cmd.msg;
			textmsg = textmsg.replace(/\[[0-9]*\](.*)/,"$1");
			_vidi.conf.usercallback("received_textchat", {
				message: textmsg
			});
			break;
		case "property_set":
			//nothing to do
			var set = {
				where: cmd.where,
				id: cmd.id,
				key: cmd.key,
				value: cmd.value
			};
			_vidi.conf.usercallback("property_set", set);
			// do automatic updates from showPublicProperty
			var showinfo_i; var showInfo;
			if (_vidi.instances.properties[set.where] !== undefined) {
				if (_vidi.instances.properties[set.where][set.id] !== undefined) {
					if (_vidi.instances.properties[set.where][set.id][set.key] !== undefined) {
						for (showinfo_i in _vidi.instances.properties[set.where][set.id][set.key]) {
							if ((showInfo = _vidi.instances.properties[set.where][set.id][set.key][showinfo_i]) !== undefined) {
								var formatter = showInfo.formatter;
								var elementid = showInfo.elementid;
								var update = {
									where : set.where,
									id : set.id,
									key : set.key,
									value : set.value,
									preformatted_value : set.value,
									formatter : formatter,
									elementid : elementid
								};
								if (formatter !== undefined && formatter !== null) {
									update.value = formatter(update);
								}
								if (elementid) {
									// TODO: should we always do innerHTML? Or should this change based on the type of element?
									document.getElementById(elementid).innerHTML = update.value;
								}
							}
						}
					}
				}
			}
			break;
		case "inputstarted":
			_vidi.conf.usercallback("started_publishing"); // TODO: remove deprecated
			_vidi.conf.usercallback("input_started");
			break;
		case "outputstarted":
			_vidi.conf.usercallback("play_start"); // TODO: remove deprecated
			_vidi.conf.usercallback("output_started");
			break;
		case "outputstopped":
			_vidi.conf.usercallback("output_stopped");
			break;
		case "inputstopped":
			// TODO: not implemented in flash side
			_vidi.conf.usercallback("input_stopped");
			break;
		case "server_says":
			var servermsg = cmd.msg;
			_vidi.conf.usercallback("server_says", {
				message: servermsg
			});
			break;
		case "nocamera":
			_vidi.conf.usercallback("no_camera");
			break;
		case "nomicrophone":
			_vidi.conf.usercallback("no_microphone");
			break;
		case "event":
			var eventmsg = cmd.status_code;
			_vidi.conf.usercallback(eventmsg);
			if (eventmsg == "connect")  {
				_vidi.io.is_connected = true;
			}
			break;
		default:
			_vidi.apilog("Unexpected command-%o from flash, API don't know how to handle it", msg);
		}
	},
	getMovieObject: function getMovieObject(obj_name) {
		return _vidi.conf[obj_name] === undefined ? {} : _vidi.conf[obj_name];
	}
};

/* vidi js api */
vidi = function() {
	var minFlashVersion = "9.0.115";

	var get_urls = function () {
		var scripts = document.getElementsByTagName("script");
		var script, vidi_js_path;
		for (var i = 0; i<scripts.length; i++) {
			if (true) { // hack to pass jslint
				script = scripts[i];
				if (script.src && script.src.match("vidi.js")) {
					vidi_js_path = script.src;
				}
			}
		}
		if (!vidi_js_path) {
			_vidi.apilog("Cannot find vidi.js path");
			return false;
		}
		// Now we found th path of vidi.js
		var urls = {};
		urls.vidi_js = vidi_js_path;
		urls.vidi_server_url = vidi_js_path.split("static")[0]; // http://hostname:5080/vidi/
		urls.vidi_swf = urls.vidi_server_url + "static/vidi.swf";
		urls.swfobject_url = urls.vidi_server_url + "static/swfobject.js";
		return urls;
	};

	var check_in_list = function (oo, oo_var_list) {
		for (var i in oo) {
			if (i) {
				var found=false;
				for (var j in oo_var_list) {
					if (j) {
						if (i == oo_var_list[j]) {
							found = true;
						}
					}
				}
				if (!found) {
						return i;
				}
			}
		}
		return null;
	};

	var construct_flashvars = function (obj) {
		var flashvars = "";
		var value;
		// the following if block is for transforming
		// true to 1 and false to 0 to make them usable
		// within actionscript..
		for (var key in obj) {
			if (!(obj[key] === undefined))  {
				if (obj[key] === true) {
					value = 1;
				} else if (obj[key] === false) {
					value = 0;
				} else {
					value = obj[key];
				}
				flashvars += flashvars ?
					"&" + key + "=" + value : key + "=" + value;
			}
		}
		return flashvars;
	};

	var BaseScreen = function(parentid, width, height, specific_flashvars, swmode) {

		// Private Variables
		var expressInstallSwfUrl = false;
		var swfid = "vidi_" + parentid;
		var flashvars = specific_flashvars ?
			specific_flashvars + "&swfid=" + swfid :
			"swfid=" + swfid;
		var params = {allowScriptAccess: "always", allowFullScreen: "true", wmode: swmode, flashvars: flashvars};
		var attributes = {"id": swfid, "name": swfid};
		// We couldn't have _vidi.instances.masterscreen yet, so
		// use this screen's swfid as masterswfid if there isn't any yet.
		var masterswfid = (_vidi.instances.masterscreen && _vidi.instances.masterscreen.swfid) || swfid;
		params.flashvars += "&masterswfid=" + masterswfid;
		params.flashvars += "&screen_width=" + width;
		params.flashvars += "&screen_height=" + height;
		params.flashvars += "&debug=" + _vidi.conf.debug;

		// Private Methods
		// no private methods for now

		// Action Time (consider as constructor part)
		_vidi.apilog("Creating flash in div[%o] with " +
						"flashvars[%o], masterswfid[%o]",
						parentid, params.flashvars, masterswfid);

		swfobject.embedSWF(get_urls().vidi_swf,
						parentid, width, height,
						minFlashVersion,
						expressInstallSwfUrl,
						false, // passing flashvars from params, people says it's faster.
						params,
						attributes);

		var modify_var_list;
		modify_var_list = ["camera", "screen", "speaker", "mic", "background_image", "disconnected_image",
						   "screen_smoothing", "camera_fps", "camera_quality", "camera_bandwidth",
						   "camera_favorarea", "camera_keyframeinterval", "mic_gain", "mic_rate",
						   "mic_silencelevel", "mic_silencetimeout", "speaker_volume", "screen_width", "screen_height",
						   "localecho_mirror"
					   ];

		// Public Properties and Methods
		return {
			swfid: swfid,

			/*
			 * Function: screen.start()
			 *
			 * Start playing of video/audio. Screens automatically start playing, users should call this function only if they called stop() manually.
			 */
			start: function start() {
				_vidi.io.sendCmdToFlash(swfid, "start");
				return true;
			},

			/*
			 * Function: screen.stop()
			 *
			 * Stops playing of video/audio. Screen freezes when stopped.
			 */
			stop: function stop() {
				_vidi.io.sendCmdToFlash(swfid, "stop");
				return true;
			},

			/*
			 * Function: screen.destroyScreen()
			 *
			 * Closes screen, and destroys the flash inside the screen
			 */
			destroyScreen: function destroyScreen() {
				_vidi.io.sendCmdToFlash(swfid, "destroyScreen");
				swfobject.removeSWF(swfid);
				// TODO remove from Vidi instances ;
				return true;
			},

			/*
			 * Function: screen.sendTextMsg(msg)
			 *
			 * Send message to all room attandees.
			 *
			 * Params:
			 *    msg - Text message to be sent.
			 */
			sendTextMsg: function sendTextMsg(oo) {
				var randomNumber = Math.floor(Math.random() * 1001);
				var newmsg = "[" + randomNumber + "]" + oo.msg;
				_vidi.io.sendCmdToFlash(swfid, "sendTextMessage", {message: newmsg});
				return true;
			},

			/*
			 * Function: screen.tellServer(oo)
			 *
			 * Send message to server.
			 *
			 * Params:
			 *    oo.message - message to be sent.
			 */
			tellServer: function tellServer(oo) {
					if (oo.message === undefined) {
						 _vidi.throwerror("message was empty. please specify a message to send. eg: tellServer({message:'hi'});");
					} else {
						_vidi.io.sendCmdToFlash(swfid, "tellServer", {message: oo.message});
					}
				return true;
			},

			/*
			 * Function: screen.tellClient(oo)
			 *
			 * Send message to server.
			 *
			 * Params:
			 *    oo.clientid - client that will receive message
			 *    oo.msg - message to be sent.
			 */
			tellClient: function tellClient(oo) {
				var randomNumber = Math.floor(Math.random() * 1001);
				var newmsg = "[" + randomNumber + "]" + oo.msg;
				_vidi.io.sendCmdToFlash(swfid, "tellClient", {clientid: oo.clientid, msg: newmsg});
				return true;
			},

			/*
			 * Function: screen.modify(oo)
			 *
			 * Function to modify properties of flash. Options object
			 * takes the same options as options in createVidi(),
			 * however, currently, only the following parameters can
			 * be modified
			 *
			 * Parameters:
			 *    oo - Options object
			 *
			 * Options object takes the following (optional):
			 *    oo.camera - Boolean
			 *    oo.mic - Boolean
			 *    oo.mic_volume - Int
			 *    oo.speaker_volume - Int
			 *
			 * Returns:
			 *    Void
			 */
			modify: function modify(oo) {
				 var thisfunc = "modify";
				 var wrong_parameter =  check_in_list(oo,modify_var_list);
				 if (wrong_parameter) {
					 _vidi.throwerror("parameter '" + wrong_parameter + "' to "+ thisfunc +" is not a recognized parameter");
				 }

				_vidi.apilog("[vidi.js] modify with parameters = %o",oo);
				_vidi.io.sendCmdToFlash(swfid, "modify", oo);
			},

			/*
			 * Function: screen.getStatus(oo)
			 *
			 * Function to ?? 
			 *
			 * Parameters:
			 *    oo - Options object
			 *
			 *    Void
			 */
			getStatus: function getStatus(oo) {
				var thisfunc = "getStatus";
				var wrong_parameter =  check_in_list(oo,modify_var_list);
				if (wrong_parameter) {
				    _vidi.throwerror("parameter '" + wrong_parameter + "' to "+ thisfunc +" is not a recognized parameter");
				}

				_vidi.apilog("getStatus with parameters = %o",oo);
				return _vidi.io.sendCmdToFlash(swfid, "getStatus", oo);
			},


			/*
			 * Function: screen.showPublicProperty(oo)
			 *
			 * Function to display and automatically display updates to a public
			 * property.
			 *
			 * Parameters:
			 *    oo.where - where the property is being set. can be "*" to get all.
			 *    oo.id - id of property object. can be "*" to get all.
			 *    oo.key - key of the property. can be "*" to get all.
			 *
			 * Optional:
			 *    oo.formatter - a function that is called when property is updated. can also reformat the output
			 *    oo.elementid - which element the output should be put in. If divid is empty, not shown on in div.
			 *
			 * The function displays whatever is returned from this function. If no div is specified, output is not shown.
			 *
			 * Returns:
			 *    Void
			 */
			showPublicProperty: function showPublicProperty(oo) {
				var thisfunc = "showPublicProperty";
				var public_property_var_list = [ "where", "id", "key", "elementid", "formatter" ];
				var wrong_parameter =  check_in_list(oo,public_property_var_list);
				if (wrong_parameter) {
				    _vidi.throwerror("parameter '" + wrong_parameter + "' to "+ thisfunc +" is not a recognized parameter");
				}
				if (!oo.where) {
					_vidi.throwerror("option where is a required parameter of "+thisfunc);
				}
				if (oo.id === undefined) {
					_vidi.throwerror("option id is a required parameter of "+thisfunc);
				}
				if (!oo.key) {
					_vidi.throwerror("option key is a required parameter of "+thisfunc);
				}
				if (!oo.formatter && !oo.elementid) {
					_vidi.throwerror("either a formatter or elementid option is required as a parameter of "+thisfunc);
				}
				var elementid = null;
				if (oo.elementid) {
					elementid = oo.elementid;
				}
				if (elementid) {
					if (! document.getElementById(elementid)) {
						_vidi.throwerror(" The element given by the 'eleementid' parameter (elementid:"+oo.elementid+") could not be found in the document. ie: document.getElementbyId("+elementid+")");
					}
				}

				var formatter = null;
				if (oo.formatter) {
					formatter = oo.formatter;
				}
				_vidi.apilog("getStatus with parameters = %o",oo);
				if (_vidi.instances.properties[oo.where] === undefined) {
					_vidi.instances.properties[oo.where]={};
				}
				if (_vidi.instances.properties[oo.where][oo.id] === undefined) {
					_vidi.instances.properties[oo.where][oo.id]={};
				}
				if (_vidi.instances.properties[oo.where][oo.id][oo.key] === undefined) {
					_vidi.instances.properties[oo.where][oo.id][oo.key]=[];
				}
				_vidi.instances.properties[oo.where][oo.id][oo.key].push( { formatter: formatter, elementid:elementid });
			},

			/*
			 * Function: screen.getVersion()
			 *
			 * Function to get vidi server version.
			 *
			 * Returns:
			 *    String
			 */
			getVersion: function getVersion() {
				return _vidi.io.sendCmdToFlash(swfid, "getVersion");
			},

			/*
			 * Function: screen.playSound(oo)
			 *
			 * Function to play sound from given url.
			 *
			 * Parameters:
			 * 	   oo.sound
			 *
			 * Returns:
			 * 	   void
			 */
			playSound: function playSound(oo) {
				var thisfunc = "playSound";
				_vidi.io.sendCmdToFlash(swfid, "playSound", oo);
			}
		};
	};

	// needed for IE compability and dynamic swfobject creation
	/*jslint evil:true */
	document.write('<script src="' + get_urls().swfobject_url + '" type="text/javascript"></script>');
	/*jslint evil:false */
	
	var init_var_list;
	init_var_list = ["clientid", "roomid" , "callback", "debug"];

	var screen_var_list;
	screen_var_list = ["outputid", "inputid", "divid",
		"camera", "screen", "speaker", "mic", "bwcheck",
		"localecho", "background_image", "disconnected_image",
		"screen_height", "screen_width", "screen_buffertime",
		"screen_deblocking", "screen_fps", "screen_smoothing",
		"camera_motionlevel", "camera_motiontimeout", "camera_fps",
		"camera_quality", "camera_bandwidth", "camera_favorarea",
		"camera_keyframeinterval", "camera_localcompress",
		"camera_width", "camera_height",
		"mic_gain", "mic_rate",
		"mic_silencelevel", "mic_silencetimeout",
		"localecho_mirror"
		];
	
	//public fuctions  of vidi
	return {

		/*
		 * Function: vidi.initialize(oo)
		 *
		 * Function to initialize vidi api.
		 *
		 * Parameters:
		 *    oo - Options object
		 *
		 * Options object takes the following (required):
		 *    oo.clientid - Got from webservice.
		 *    oo.roomid - Got from webservice.
		 *
		 * Options may also take the following optional parameters:
		 *    oo.callback - User defined global javascript callback function which api will inform customer via this callback. (default is null)
		 *    oo.debug - Sends debug messages to firebug/lite (default is false)
		 *
		 * oo.callback will be triggered by vidi.js api, with the following parameters under specified circumstances:
		 *    oo.callback("callback_test"); - Tests callback function, function must return "ok"
		 *    oo.callback("old_or_no_flash"); - Flash player not installed in client or version is older than required. API users must handle this and return "old_or_no_flash_ok" or vidi will show an alert.
		 *    oo.callback("flash_loaded", {divid: <divid>} ); - Flash object was loaded into div.
		 *    oo.callback("output_stopped"); - The subscribed video source disappeard, means Flash screen will be suspended.
		 *    oo.callback("connect"); - Connection done.
		 *    oo.callback("disconnected_from_video_server"); - Disconnected from video server.
		 *    oo.callback("insufficient_bandwidth"); - BW is insufficient for streaming.
		 *    oo.callback("server_says", message); - User defined callback for pushing messages from serverside to clientside (like comet).
		 *    oo.callback("no_camera"); - Camera needed but not exist.
		 *    oo.callback("no_microphone") - Microphone needed but not exist.
		 *    oo.callback("play_reset"); - Playing of stream reset.
		 *    oo.callback("output_started"); - Playing of stream started.
		 *    oo.callback("published"); - Current user's video stream publishing started.
		 *    oo.callback("received_textchat", {message: textmsg} ); - Text chat messages of the room will be delivered by this call.
		 *    oo.callback("started_input"); - Current user's video stream publishing will be started.
		 *    oo.callback("unpublished"); - Current user's video stream publishing stopped.
		 *    oo.callback("unpublish_notify"); - Current user's video stream publishing will be stopped.
		 *    oo.callback("property_set"); - a property has been set
		 *    oo.callback("play_start"); - DEPRECATED, use output_started instead.
		 *    oo.callback("started_publishing"); - DEPRECATED. use input_started instead.
		 *
		 * Returns:
		 *    Void
		 */
		initialize: function initialize(oo) {
			var thisfunc = "vidi.initialize()";
			if (_vidi.conf.initialized) {
				_vidi.throwerror("Vidi already initialized by " + thisfunc + ". This function should be called only for once.");
			}

			if (oo.debug !== undefined) {
				_vidi.conf.debug = oo.debug;
			}
			/* Hack for firebug debugging in OPERA and IE */
			if (_vidi.conf.debug &&
			    (!window.console || window.opera || document.all)) {
				window.alert('for debug, load http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js into your page');
			}

			var wrongParam = check_in_list(oo,init_var_list);
			if (wrongParam) {
				_vidi.throwerror("parameter '" + wrongParam + "' to "+ thisfunc +" is not a recognized parameter");
			}
			if (!oo.clientid) {
				_vidi.throwerror("option clientid is a required parameter of "+thisfunc);
			}
			if (!oo.roomid) {
				_vidi.throwerror("option roomid is a required parameter of "+thisfunc);
			}
			_vidi.conf.clientid = oo.clientid;
			_vidi.conf.roomid = oo.roomid;
			if (oo.callback) {
				// Try calling the call back function, to make sure it exist
				// otherwise, this will probably give an error.
				try {
					var tor = oo.callback("callback_test");
					if ( tor != "ok") {
						_vidi.throwerror("Callback function given to "+thisfunc+" should return 'ok'" +
							" when called with callback_test. " + oo.callback + "('callback_test')"+
							" returned '" + tor + "' instead of 'ok'");
					}
				} catch (e) {
					_vidi.throwerror("You gave callback function "+oo.callback+" as a parameter to "+thisfunc+", but callback could not be called: " + e);
				}
				_vidi.conf.usercallback = _vidi.calllater(oo.callback);
			}

			var flashVersion = swfobject.getFlashPlayerVersion();
			var currentFlashVersion = flashVersion.major + "." + flashVersion.minor + "." + flashVersion.release;
			_vidi.apilog("currentFlashVersion = %o, minFlashVersion = %o", currentFlashVersion, minFlashVersion);
			// checks if flash version is higher or equal(i.e. not only equal) to minFlashVersion
			if (!swfobject.hasFlashPlayerVersion(minFlashVersion)) {
				_vidi.apilog("ERROR: old flash version or no flash installed");
				// we cant use _vidi.conf.usercallback here because it
				// returns undefined immediately and we cant check
				// return value.
				var result = oo.callback("old_or_no_flash", {
						"currentFlashVersion": currentFlashVersion,
							"minFlashVersion": minFlashVersion
				});
				if (result != "old_or_no_flash_ok") {
					window.alert("You should install Adobe Flash Player (min. ver:" + minFlashVersion + "). You can install Flash player from http://get.adobe.com/flashplayer/");
				}
			}
			// check to prevent from being loaded from localhost or 127.0.0.1... actionscript bridge doesn't work with localhost. HACKish
			// http://www.actionscript.org/forums/showthread.php3?t=120433
			var host = document.location.host;
			if (host && (host == "localhost" || host == "127.0.0.1")) {
				_vidi.apilog("ERROR: vidi will not work inside a page with localhost or 127.0.0.1");
				window.alert("vidi will not work inside a page with localhost or 127.0.0.1");
				_vidi.throwerror("vidi will not work inside a page with localhost or 127.0.0.1");
			}

			_vidi.conf.initialized = true;
			return vidi;
		},

		/*
		 * Function: vidi.createVidi(oo)
		 *
		 * Function for creating videochat, audiochat or
		 * localecho. Each screen instance will be held as
		 * _vidi.instances.screens[divid]. If the screen is the
		 * first one, it will be the master swf automatically.
		 *
		 * Parameters:
		 *    oo - Options object
		 *
		 * Options object takes the following:
		 *    oo.inputid - Got from webservice.
		 *    oo.outputid - Got from webservice.
		 *    oo.localecho - Boolean value if this screen will be a localecho.
		 *
		 *
		 * The following are optional parameters:
		 *    oo.mic - Boolean value for activating the microphone. (default: true)
		 *    oo.camera - Boolean value for activating the camera. (default:screen_var_ true)
		 *    oo.speaker - Boolean value for activating the speaker. (default: true)
		 *    oo.screen - Boolean value for activating the screen. (default: true)
		 *    oo.screen_width - Width of screen that will be created. (default: div size)
		 *    oo.screen_height - Height of screen that will be created. (default: 3/4*screen_width)
		 *    oo.screen_buffertime - Specifies how long to buffer messages before starting to display the stream, in seconds (default: 0.1)
		 *    oo.screen_smoothing - Boolean value for activating screen smoothing to avoid pixelated look. (default: true)
		 *    oo.screen_deblocking - between 0 to 5. Indicates the type of filter applied to decoded video as part of post-processing (0: Lets the video compressor apply the deblocking filter as needed. 1: Does not use a deblocking filter. 2: Uses the Sorenson deblocking filter. 3: For On2 video only, uses the On2 deblocking filter but no deringing filter. 4: For On2 video only, uses the On2 deblocking and deringing filter. 5: For On2 video only, uses the On2 deblocking and a higher-performance On2 deringing filter.) (default: 0)
		 *    oo.screen_fps - Specifies the frame rate for incoming video, to stop receiving video, pass 0 for FPS (default: is determined by stream publisher)
		 *    oo.camera_width, oo.camera_height - Dimensions (pixels in integer) that will be used for capturing video (default: screen size)
		 *    oo.camera_fps - Frames per second to be recorded in the camera (default: 15)
		 *    oo.camera_quality - Quality of camera (from 1=maximum compress  to 100=no compression). 0 means never exceed bandwidth limit (default: 50)
		 *    oo.camera_bandwidth - Maximum bandwidth of camera in kilobit/s(0 means: maintain quality, no maximum) (default: 0)
		 *    oo.camera_favorarea - Boolean. true if you prefer larger area, false if you prefer higher fps. (default true)
		 *    oo.camera_keyframeinterval - between 1 to 48. Every nth frame will be a key frame. lower kfi->higher bw, shorter wait (default)
		 *    oo.camera_motionlevel - Between 0 to 100. The amount of motion required to invoke the activity event. (default: 50)
		 *    oo.camera_motiontimeout - The number of milliseconds between the time the camera stops detecting motion and the time the activity event is invoked. (default: 2000)
		 *    oo.camera_localcompress - Boolean. true if you would like to show localecho as it would appear on the other side. uses more local cpu. (default: false)
		 *    oo.mic_gain - between 1 to 100. the amount by which the microphone should multiply the signal before transmitting (default: 50)
		 *    oo.mic_rate - allowed values: 5, 8, 11, 22 or 44. The rate at which the microphone captures sound, in kHz.
		 *    oo.mic_silencelevel - between 1 te 100. The amount of sound required to activate the microphone and dispatch the activity event. (default: 0)
		 *    oo.mic_silencetimeout - The number of milliseconds between the time the microphone stops detecting sound and the time the activity event is dispatched. (default: 10000)
		 *    oo.background_image - Background image which will be displayed when there is no screen or video (default: false)
		 *    oo.disconnected_image - An image which will be displayed when the other side disconnects (default: false)
		 *    oo.bwcheck - Boolean. checks bandwidth status to reduce usage if bandwidth is insufficient (default: false)
		 *    oo.localecho_mirror - Boolean. Localecho is mirrored by default. This is more comfortable for users, they should be relaxed as they looking mirror. (default: true)
		 *
		 * Returns:
		 *    Screen object.
		 */
		createVidi: function createVidi (oo) {
			var thisfunc="vidi.createVidi()";
			var initfunc="vidi.initialize()";
			var divId;

			//TODO: check for all possible combinations later on

			if (!_vidi.conf.clientid || !_vidi.conf.roomid) {
				// roomid or clientid not initialized. probably the api users did not call initialize()
				_vidi.throwerror("roomid/clientid not initialized. Please use "+initfunc+" before calling "+thisfunc);
			}

			if (!oo.divid) {
				_vidi.throwerror("option divid is a required parameter of "+thisfunc);
			}

			divId = oo.divid;
			if (! document.getElementById(divId)) {
				_vidi.throwerror(" The div element given by the 'divid' parameter (divid:"+oo.divid+") could not be found in the document. ie: document.getElementbyId("+divId+")");
			}

			// figure out camera_width and camera_height if it is on
			// figure out screen_width and screen_height if they are not given
			var swidth;
			if (oo.screen_width === undefined) {
				if (document.getElementById(divId) === undefined ||
					document.getElementById(divId).clientWidth === undefined) {
						_vidi.throwerror("Could not figure out the width"+
								    " of the div given by the 'divid' parameter (divid:"+
								    oo.divid+"). You can try specifying screen_width");
				} else {
					swidth = document.getElementById(divId).clientWidth;
				}
			} else {
				swidth = oo.screen_width;
			}

			var sheight;
			if (oo.screen_height === undefined) {
				sheight = swidth*3/4;
			} else {
				sheight = oo.screen_height;
			}
			var scamwidth = oo.camera_width;
			var scamheight = oo.camera_height;

			// http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/media/Camera.html
			// Important: Flash Player displays a Privacy dialog box
			// that lets the user choose whether to allow or deny
			// access to the camera. Make sure your application window
			// size is at least 215 x 138 pixels; this is the minimum
			// size required to display the dialog box.
			if (oo.camera || oo.mic) { // if we access any source that needs permission
				if (swidth < 215 || sheight < 138) {
					_vidi.throwerror(
						"Requested screen size " +
							"screen_width:" + swidth + ", screen_height:" + sheight +
							" you sent to " + thisfunc +
							" is smaller than the minimum required size (215 x 138)," +
							" This size is needed for flash to ask for cam/mic permission.");
				}
			}

			var scamera = oo.camera || (oo.camera === undefined) ? _vidi.conf.camera : false;
			var smic = oo.mic || (oo.mic === undefined) ? _vidi.conf.mic : false;
			var sspeaker = oo.speaker || (oo.speaker === undefined) ? _vidi.conf.speaker : false;
			var sscreen = oo.screen || (oo.screen === undefined) ? _vidi.conf.screen : false;
			var clientid = _vidi.conf.clientid;
			var roomid = _vidi.conf.roomid;
			var background_image = oo.background_image;		
			var disconnected_image = oo.disconnected_image;		
			var screen_smoothing = oo.screen_smoothing;
			var screen_buffertime = oo.screen_buffertime;
			var screen_deblocking = oo.screen_deblocking;
			var screen_fps = oo.screen_fps;
			var camera_fps = oo.camera_fps;
			var camera_quality = oo.camera_quality;
			var camera_bandwidth = oo.camera_bandwidth;
			var camera_keyframeinterval = oo.camera_keyframeinterval;
			var camera_favorarea = oo.camera_favorarea;
			var camera_motionlevel = oo.camera_motionlevel;
			var camera_motiontimeout = oo.camera_motiontimeout;
			var camera_localcompress = oo.camera_localcompress;
			var mic_gain = oo.mic_gain;
			var mic_rate = oo.mic_rate;
			var mic_silencelevel = oo.mic_silencelevel;
			var mic_silencetimeout = oo.mic_silencetimeout;
			var bwcheck = oo.bwcheck;
			var localecho_mirror = oo.localecho_mirror || (oo.localecho && oo.localecho_mirror === undefined) ? true : false;
			var newscreen;
			_vidi.apilog("DEBUG: createVidi([%o])", oo);

			if (!oo.background_image && (scamera || sscreen)) {
				_vidi.apilog("[WARNING] There is no background image. Consider using background_image: parameter to "+thisfunc);
			}

			// Parameter check
			//
			// NOTE: Because, there should be some other needs related with cases and implementing
			// as a if tree reduces running time, I used nested if trees instead of logical "and"
			
			var wrongParameter =  check_in_list(oo,screen_var_list);
			if (wrongParameter){
				_vidi.throwerror("parameter '" + wrongParameter + "' to "+ thisfunc +" is not a recognized parameter");
			}

			// TODO: createVidi should also take a debug parameter which turns on debug for that screen

			var swmode;
			if(!scamera && !smic){
				swmode = "transparent";
			} else {
				swmode = "window";
			}

			if (!oo.inputid && !oo.localecho) {
				if (!!smic) {
					if (oo.mic !== undefined) {
						_vidi.throwerror(thisfunc+": if mic:true, you must send an inputid or specify localecho:true. This is where the sound from the mic will be sent");
					} else {
						_vidi.throwerror(thisfunc+": if you do not want the mic, please specify mic:false. If you want the mic, you must send an inputid or specify localecho:true. This is where the sound from the mic will be sent");
					}
				}
				if (!!scamera) {
					if (oo.camera !== undefined) {
						_vidi.throwerror(thisfunc+": if camera:true, you must send an inputid or specify localecho:true. This is where the video from the camera will be sent");
					} else {
						_vidi.throwerror(thisfunc+": if you do not want the camera, please specify camera:false. If you want the camera, you must send an inputid or specify localecho:true. This is where the video from the camera will be sent");						
					}
				}
			}
			
			if (!oo.outputid && !oo.localecho) {
				if (!!sscreen) {
					if (oo.screen === undefined) {
						_vidi.throwerror(thisfunc+"please specify what you want to see: 1) an outputid, or 2) specify localecho:true. To see nothing specify screen:false");
					} else {
						_vidi.throwerror(thisfunc+"you requested screen:true. please specify what you want to see: 1) an outputid, or 2) specify localecho:true. To see nothing specify screen:false instead");
					}
				}
				if (!!sspeaker) {
					if (oo.screen === undefined) {
						_vidi.throwerror(thisfunc+": please specify what you want to hear: 1) an outputid, or 2) specify localecho:true. To see hear nothing specify speaker:false");
					} else {
						_vidi.throwerror(thisfunc+": you requested speaker:true. please specify what you want to hear: 1) an outputid, or 2) specify localecho:true. To hear nothing specify speaker:false instead");
					}
				}
			}
			
			if (!!oo.localecho) {
				if (!!oo.outputid) {
					_vidi.throwerror(thisfunc+": localecho:true and outputid can not be set together: you requested localecho=" + oo.localecho + " outputid=" + oo.outputid+ ". Choose one or the other");
				} else {
					if (!scamera && !smic) {
						// throw exception, at least one of them must exists
						_vidi.throwerror(thisfunc+": if localecho:true, at least a camera or mic must be true. But you requested: localecho:"+!!oo.localecho+" mic:" + smic + " camera:" + scamera);
					}
					if (!sscreen && !sspeaker) {
						// throw exception, at least one of them must exists
						_vidi.throwerror(thisfunc+": if localecho:true, at least a screen or speaker must be true. But you requested: localecho:"+!!oo.localecho+" screen:" + sscreen + " speaker:" + sspeaker);
					}
					if (!!sscreen && !(!!sscreen == !!scamera)) {
						// screen and camera need to be set together
						_vidi.throwerror(thisfunc+": if localecho:true, screen and camera should set together, but they are different. You requested: screen:" + sscreen + " camera:" + scamera);
					}
					if (!!sspeaker && !(!!sspeaker == !!smic)) {
						// speaker and mic need to be set together
						_vidi.throwerror(thisfunc+": if localecho:true, speaker and mic should set together, but they are different. You requested: speaker:" + sspeaker + " mic:" + smic);
					}
				}
			}
			
			if (!!oo.inputid) {
				if (!smic && !scamera) {
					// throw exception, at least one of them must exists
					_vidi.throwerror(thisfunc+": if inputid is given, at least one of mic or camera should be set. You requested: inputid:"+oo.inputid+" mic:"+smic+" camera:"+scamera);
				}
			}
			
			if (!!oo.outputid) {
				if (!sscreen && !sspeaker) {
					// throw exception, at least one of them must exists
					_vidi.throwerror(thisfunc+": if outputid is given, at least one of screen or speaker should be set. You requested: outputid:"+oo.outputid+" screen:" + sscreen + " speaker:" + sspeaker);
				}
			}
			
			if (!!oo.localecho_mirror) {
				if (!oo.localecho) {
					_vidi.throwerror(thisfunc+": localecho_mirror can only be used with localecho option.");
				}
			}
			var fvars = {"mic": smic, "camera": scamera, "speaker": sspeaker,"localecho": oo.localecho,
				 "screen": sscreen, "background_image": background_image,
				 "disconnected_image":disconnected_image, "bwcheck": bwcheck,
				 "screen_buffertime": screen_buffertime, "screen_smoothing": screen_smoothing,
				 "screen_deblocking": screen_deblocking, "screen_fps": screen_buffertime,
				 "camera_width": scamwidth, "camera_height": scamheight,
				 "camera_quality": camera_quality, "camera_localcompress": camera_localcompress,
				 "camera_fps": camera_fps, "camera_bandwidth": camera_bandwidth,
				 "camera_keyframeinterval": camera_keyframeinterval,
				 "camera_favorarea": camera_favorarea,
				 "camera_motionlevel": camera_motionlevel,
				 "camera_motiontimeout": camera_motiontimeout,
				 "loader_vidi_js_url": get_urls().vidi_js,
				 "clientid": clientid, "roomid": roomid,
				 "inputid": oo.inputid, "outputid": oo.outputid,
				 "mic_gain": mic_gain, "mic_rate": mic_rate,
				 "mic_silencelevel": mic_silencelevel, "mic_silencetimeout": mic_silencetimeout,
				 "localecho_mirror": localecho_mirror
			};

			newscreen = new BaseScreen(divId, swidth, sheight, construct_flashvars(fvars), swmode);

			_vidi.instances.screens[newscreen.swfid] = newscreen;
			_vidi.apilog("current _vidi.instances[%o]", _vidi.instances);
			
			if (! _vidi.instances.masterscreen) {
				_vidi.apilog("DEBUG: setting screen with swfid-'%o' as masterscreen", newscreen.swfid);
				_vidi.instances.masterscreen = newscreen;
			}
			return newscreen;
		},
		createScreen: function createScreen (oo) {
			return this.createVidi(oo);
		}
	};
}();

_vidi.apilog("vidi.js loaded successfully");
