//////////////////////////////////////////////////////////////////////////////////////////

function AppEnvironment(env) {
  this.env = env;
  
  this.isStandalone = function() {
    return env.indexOf("public-") == 0;
  }
}

var appEnv;

var reqParams = getRequestParameters();

var log = log4javascript.getLogger();
var loggingLevel = "DEBUG";
var logLevels = {
  TRACE: log4javascript.Level.TRACE,
  DEBUG: log4javascript.Level.DEBUG,
  INFO:  log4javascript.Level.INFO,
  WARN:  log4javascript.Level.WARN,
  ERROR: log4javascript.Level.ERROR,
  FATAL: log4javascript.Level.FATAL,
  OFF:   log4javascript.Level.OFF
};

// global reference to popup appender - necessary to close logs popup on body unload event
var popUpAppender = null;

var browser = BrowserDetect.browser;
var browserVersion = BrowserDetect.version;
var isIE = (browser == "Explorer");
var isWin = swfobject.ua.win;
var isMac = swfobject.ua.mac;

var flashVersion = null;

// reference to loaded SWF object
var saymama = null;

// used when referencing assets with relative urls
// this is here to enable testing in local file (see html-template/index.template.html)
var pathBase = "";

//////////////////////////////////////////////////////////////////////////////////////////
// Application
//////////////////////////////////////////////////////////////////////////////////////////

function loadApplicationSWF(swfPath) {

  log.info(browser + " " + browserVersion);
  
  flashVersion = getFlashPlayerVersion();
  if (flashVersion == "0.0.0") {
    log.warn("Flash Player is not installed");
    swfobject.addDomLoadEvent(displayNoFlashInfo);
    return;
  }

  log.info("Installed Flash Player version: ", flashVersion);
  swfobject.addDomLoadEvent(setFlashVersion);

  var minFlashVersion = "10.1.82";
  // require 10.2 for Mac OS users
  if (swfobject.ua.mac) {
    // check for Mac OS X Lion
    if (navigator.userAgent.indexOf("10.7") != -1       // FF
      || navigator.userAgent.indexOf("10_7") != -1) {   // Chrome, Safari
      minFlashVersion = "10.3.183";
    } else {
      minFlashVersion = "10.2.0";
    }    
  }
  log.info("Required Flash Player version: ", minFlashVersion);

  var flashvars = reqParams;

  var params = {
    allowFullScreen:    "true",
    allowScriptAccess:  "sameDomain",
    bgcolor:            "#333333",
    wmode:              "opaque"
  };

  var attributes = {
    id:   "saymama",
    name: "saymama"
  };

  swfobject.embedSWF(swfPath, "flash_content", "100%", "100%", minFlashVersion,
      pathBase + "expressInstall.swf", flashvars, params, attributes, onSWFEmbed);

  // TODO perhaps it can be done elsewhere ?
  //swfmacmousewheel.registerObject(attributes.id);
  
  OAuth.setFlashElement(attributes.id);
}

function showFooter() {
	var fade = 0;
	var footer = document.getElementById("flash_footer").style;
	var ms = (footer.opacity == 0) ? 0 : 1;
	var speed = setInterval(Fade, 10);

	function Fade() {
		if (fade < 100) {
		fade += 1;
		if (ms) {
			footer.filter="alpha(opacity = " + fade + ")";
		} else {
			footer.opacity=(fade / 100);
			}
		} else {
			clearInterval(speed);
		}
	}
};

function displayNoFlashInfo() {
  var content = document.getElementById("content");
  	content.innerHTML = '' +
      '<img id="saymama_logo" src="' + pathBase + 'img/logo.png" alt="SayMama" />' +
      '<div id="getflash_frame">' +
      '  <div class="left"></div>' +
      '  <div class="center">' +
      '    <img src="' + pathBase + 'img/getflash_info.png" alt="You need Flash!" />' +
      '    <a class="getflash_button" href="http://www.adobe.com/go/getflashplayer" target="_blank"><img src="' + pathBase + 'img/getflash_button.png" alt="Get Adobe Flash Player" /></a>' +
      '  </div>' +
      '  <div class="right"></div>' +
      '  <div class="shadow"></div>' +
      '</div>';
}

function getFlashPlayerVersion() {
  log.debug("Checking Flash Player version...");
  var versionObj = swfobject.getFlashPlayerVersion();
  return versionObj.major + "." + versionObj.minor + "." + versionObj.release;
}

function setFlashVersion() {
  var versions_span = document.getElementById("flash_version_span");
  versions_span.innerHTML = "Flash: " + flashVersion;
}

function onSWFEmbed(e) {
  if (e.success) {
    log.debug("SWF loaded successfully");
    saymama = e.ref;
    new MouseWheelSupport(saymama);    
  } else {
    log.error("Failed to load application SWF", e);
  }
}

function invokeFlexMethod(methodName, args) {
  if (!args) {
    args = [];
  }
  log.debug("Invoking Flex callback method: " + methodName + ", args: " + print(args));
  return saymama[methodName].apply(saymama, args);
}

function print(obj) {
  return JSON.stringify(obj);
}

//////////////////////////////////////////////////////////////////////////////////////////
// Google Analytics
//////////////////////////////////////////////////////////////////////////////////////////

var _gaq = _gaq || [];

function setupAnalytics() {
  _gaq.push(['_setAccount', 'UA-300615-1']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script');
    ga.type = 'text/javascript';
    ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(ga, s);
  })();
}

//////////////////////////////////////////////////////////////////////////////////////////
// Methods called by flex app
//////////////////////////////////////////////////////////////////////////////////////////

function setFriendsNotificationsCount(count) {
    log.debug("setFriendsNotificationsCount, " + count);
    var notifications_span = document.getElementById("friends_notifications");
    if (count == "0") {
      notifications_span.style.display = "none";
      notifications_span.innerHTML = "";
    } else {     
      notifications_span.innerHTML = count;
      notifications_span.style.display = "inline";
    }
}

function changeFooterStyle(isClassic) {
    log.debug("changeFooterStyle, isClassic: " + isClassic);
    var footer = document.getElementById('flash_footer');
    if (isClassic) {
      footer.setAttribute("class", "flash_footer_classic");
      footer.setAttribute("className", "flash_footer_classic");
    } else {
      footer.setAttribute("class", "flash_footer_wrapper");
      footer.setAttribute("className", "flash_footer_wrapper");
    }
}

function showFooterLinks(show) {
  var footerLinks = document.getElementById('footer_links');
  footerLinks.style.display = show ? "block" : "none";
  var findFriendsLink = document.getElementById('find_friends_link');
  findFriendsLink.style.display = show && !appEnv.isStandalone() ? "inline" : "none";
}

function showFlexFindFriends() {
  return invokeFlexMethod("showFindFriends");
}

function showFlexSetupAssistant() {
  return invokeFlexMethod("showSetupAssistant");
}

function showFlexTourPopup() {
  return invokeFlexMethod("showTourPopup");
}

function setPluginVersion(version) {
  log.debug("setPluginVersion, " + version);
  var versions_span = document.getElementById("plugin_version_span");
  versions_span.innerHTML = "Plugin: " + version;
}

function getBrowserId() {
	return browser;
}

function getBrowserVersion() {
	return browserVersion;
}

function browserInfo() {
	return navigator.userAgent;
}

// -------------------------------------------
// tab header notifications
// -------------------------------------------

var tabHeaderTimer = -1;

function showTabHeaderNotification(title, duration) {
  if (tabHeaderTimer != -1) {
    clearTimeout(tabHeaderTimer);
    tabHeaderTimer = -1;
  }
  favicon.change(pathBase + "img/favicon_notify.gif", title);
  if (duration && duration > 0) {
    tabHeaderTimer = setTimeout("hideTabHeaderNotification()", duration);
  }
}

function hideTabHeaderNotification() {
  favicon.change(pathBase + "img/favicon.ico", "SayMama");
}

//////////////////////////////////////////////////////////////////////////////////////////
// SayMama Plugin support
//////////////////////////////////////////////////////////////////////////////////////////

// reference to SayMama plugin service
var service = null;

// reference to SayMama plugin container
var plugin = null;

var _isPluginInstalled = false;

function isPluginInstalled() {
  return _isPluginInstalled;
}

function tryLoadPlugin() {
  log.info("Loading SayMama Plugin");
  plugin = new SayMamaPlugin("pluginContainer");
  plugin.setLogger(log);
  var initialized = plugin.initialize();
  if (initialized) {
    pluginInitialized();
  }
  return initialized;
}

function startPolling() {
  log.debug("startPolling");
  plugin.startPolling(pluginInitialized);
}

function pluginInitialized() {
  log.info("Plugin initialized, creating plugin service");
  _isPluginInstalled = true;
  createService();
}

function unloadPlugin() {
  log.info("Unloading SayMama Plugin");
  service.stopLocalVideo(createResponder());
  service = null;
  var pluginContainer = document.getElementById("pluginContainer");
  pluginContainer.innerHTML = '';
  plugin = null;
}

function updatePlugin(descriptorUrl) {
  log.debug("updatePlugin (descriptorUrl: " + descriptorUrl + ")");
  plugin.update(new PluginUpdateListener(), descriptorUrl)
}

function installViaJavaApplet(jarUrl, descriptorUrl) {
  log.debug("installViaJavaApplet (jarUrl: " + jarUrl + ", descriptorUrl: " + descriptorUrl + ")");
  plugin.installViaJavaApplet(new PluginInstallerListener(), jarUrl, descriptorUrl);
}

function isExtensionInstalled() {
  log.debug("isExtensionInstalled");
  return plugin.installationExtensionInstalled();
}

function installViaExtension(descriptorUrl) {
  log.debug("installViaExtension (descriptorUrl: " + descriptorUrl + ")");
  plugin.installViaExtension(new PluginInstallerListener(), descriptorUrl);
}

function createService() {
  log.debug("Creating plugin service");
  var serviceResponder = createResponder(createServiceResult, createServiceFault);
  plugin.createService(serviceResponder);
}

function createServiceResult(smService) {
  service = smService;
  var version = service.version;
  setPluginVersion(version);
  log.debug("Plugin service created succesfully, version: " + version);
  service.addSayMamaPluginListener(createResponder(), new PluginListener());
  invokeFlexMethod("createServiceStatus", [version, null]);
}

function createServiceFault(errCode, errMsg) {
  log.error("Failed to create plugin service, errCode: " + errCode + ", errMsg: " + errMsg)
  var errorObj = {
    errorCode: errCode,
    errorMsg:  errMsg
  };
  invokeFlexMethod("createServiceStatus", [null, errorObj]);
}

function invokePluginAsyncMethod(responderId, methodName, args) {
  var responder = new FlexResponder(responderId);
  if (!args) {
    args = [];
  }
  args.unshift(responder);
  log.debug("Invoking plugin async method: " + methodName + ", responderId: " + responderId + ", args: " + print(args));
  var methodStr = "service." + methodName + "(";
  for (var i = 0; i < args.length; i++) {
  	methodStr += "args[" + i + "]";
  	if (i != args.length - 1) {
  	  methodStr += ", ";
  	}
  }
  methodStr += ")";
  log.trace("methodStr: " + methodStr);
  eval(methodStr);
}

/**
 *
 */
function PluginUpdateListener() {

  this.updateStatus = function(status) {
    log.debug("updateStatus (status: " + status + ")");
    invokeFlexMethod("updateStatus", [status]);
  };

  this.updateProgress = function(progress) {
    log.debug("updateProgress (progress: " + progress + ")");
    invokeFlexMethod("updateProgress", [parseInt(progress)]);
  };
}

/**
 *
 */
function PluginInstallerListener() {

  this.installProgress = function (progress) {
    log.debug("installProgress (progress: " + progress + ")");
    invokeFlexMethod("installProgress", [parseInt(progress)]);
  };

  this.installStatus = function (result, errorCode) {
    log.debug("installStatus (result: " + result + ", errorCode: " + errorCode + ")");
    invokeFlexMethod("installStatus", [result, errorCode]);
  };
}

/**
 *
 */
function PluginListener() {

  this.connectionStatus = function(callId, status, msg) {
    log.debug("connectionStatus (callId: " + callId + ", status: " + status + ", msg: '" + msg + "')");
    invokeFlexMethod("connectionStatus", [callId, status, msg]);
  };

  this.userStatus = function(callId, userId, connectionStatus, videoPublished, audioPublished, deviceId) {
    var result = {
      userId: userId,
      status: connectionStatus,
      audioEnabled: audioPublished,
      videoEnabled: videoPublished,
      deviceId: deviceId
    };
    log.debug("userStatus (callId: " + callId + ", status: " + print(result) + ")");
    invokeFlexMethod("userStatus", [callId, result]);
  };
  
  this.deviceListChanged = function(audioCapture, audioOutput, videoCapture) {
    log.debug("deviceListChanged (audioCapture: " + audioCapture + ", audioOutput: " + audioOutput + ", videoCapture: '" + videoCapture + "')");
    invokeFlexMethod("deviceListChanged", [audioCapture, audioOutput, videoCapture]);
  };

  this.audioPublished = function(callId, userId, isPublished) {
    log.debug("audioPublished (callId: " + callId + ", userId: " + userId + ", isPublished: '" + isPublished + "')");
    invokeFlexMethod("audioPublished", [callId, userId, isPublished]);
  };

  this.videoPublished = function(callId, userId, isPublished, deviceId) {
    log.debug("videoPublished (callId: " + callId + ", userId: " + userId + ", isPublished: '" + isPublished + "', deviceId: " + deviceId + ")");
    invokeFlexMethod("videoPublished", [callId, userId, isPublished, deviceId]);
  };

  this.videoFrameSizeChanged = function(deviceName, width, height) {
    log.debug("videoFrameSizeChanged (deviceName: " + deviceName + ", width: " + width + ", height: " + height + ")");
    invokeFlexMethod("videoFrameSizeChanged", [deviceName, width, height]);
  };

  this.micActivity = function(activityLevel) {
    log.debug("micActivity (activityLevel: " + activityLevel + ")");
    invokeFlexMethod("micActivity", [activityLevel]);
  };

  this.newVideoStats = function (roomId, userId, stats) {
    log.debug("newVideoStats (roomId: " + roomId + ", userId: " + userId + ", stats: " + stats + ")");
  };

  this.newAudioStats = function (roomId, userId, stats) {
    log.debug("newAudioStats (roomId: " + roomId + ", userId: " + userId + ", stats: " + stats + ")");
  };

  this.serviceInvalidated = function() {
    log.debug("serviceInvalidated ()");
    invokeFlexMethod("serviceInvalidated", []);
  };

  this.serviceRevalidated = function() {
    log.debug("serviceRevalidated ()");
    var version = service.version;
    setPluginVersion(version);
    invokeFlexMethod("serviceRevalidated", []);
  };
}

/**
 * Creates new async call responder. By default it creates a new instance of GenericResponder,
 * however result and error handlers can be overriden with custom functions.
 */
function createResponder(resultHandler, errorHandler) {
  var resp = new GenericResponder();
  if (resultHandler) {
    resp.result = resultHandler;
  }
  if (errorHandler) {
    resp.error = errorHandler;
  }
  return resp;
}

/**
 * Generic responder that just logs async call result/error. Can be used if we do not care about
 * the async call result.
 */
function GenericResponder() {

  this.result = function (data) {
    log.debug("[GenericResponder] resultHandler(data: " + data + ")");
  }

  this.error = function (errCode, errMsg) {
    log.error("[GenericResponder] errorHandler(errCode: " + errCode + ", errMsg: " + errMsg  + ")");
  }
}

/**
 * Responder that notifies Flex application about async call result or error.
 */
function FlexResponder(id) {

  this.id = id;

  this.result = function(data) {
    log.debug("[FlexResponder] resultHandler(responderId: " + id + ", result: " + data);
    invokeFlexMethod("flexAsyncResultHandler", [id, data]);
  }

  this.error = function(errCode, errMsg) {
    log.error("[FlexResponder] errorHandler(responderId: " + id + ", errCode: " + errCode + ", errMsg: " + errMsg);
    var errorObj = {
      errorCode: errCode,
      errorMsg:  errMsg
    }
    invokeFlexMethod("flexAsyncFaultHandler", [id, errorObj]);
  }
}

//////////////////////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////////////////////

function getRequestString() {
  var ret = '';
  var url = window.location.href;
  var idx = url.indexOf('?');
  if (idx > -1) {
    ret = url.substr(idx + 1);
  }
  return ret;
}

function getRequestParameters() {
  var swfRequestString = getRequestString();
  var requestTable = swfRequestString.split("&");
  var index = requestTable.length;
  var reqParams = {};

  while (index--) {
    var equalsIndex = requestTable[index].indexOf("=");
    var paramName = requestTable[index].substr(0, equalsIndex);
    var paramValue = requestTable[index].substr(equalsIndex + 1, requestTable[index].length);
    if (paramName != "") {
      reqParams[paramName] = paramValue;
    }
  }
  return reqParams;
}

function getLoggingLevel() {
  return loggingLevel;
}

function setupLogging() {
  // logging level can be overriden with query param
  var customLoggingLevel = reqParams["debug"];
  if (customLoggingLevel) {
    loggingLevel = customLoggingLevel;
  }
  var targetLogLevel = logLevels[loggingLevel];
  if (targetLogLevel) {
    log.setLevel(targetLogLevel);
  } else {
    log4javascript.setEnabled(false);
  }

  // if logging is enabled setup appender
  if (log4javascript.isEnabled) {
    popUpAppender = new log4javascript.PopUpAppender();
    popUpAppender.setWidth(800);
    popUpAppender.setHeight(600);
    popUpAppender.setUseOldPopUp(false);
    popUpAppender.setReopenWhenClosed(true);
    log.addAppender(popUpAppender);
  }
}

function onBodyUnload() {
  if (popUpAppender) {
    popUpAppender.close();
  }
}

