// Copyright 2009 Google Inc.  All Rights Reserved.

/**
 * A small library for running website experiments using Google Analytics
 * as a data store.
 * @author Jeremy Weinstein
 * @author Kathryn Cullen
 */


/**
 * Initializes gweb namespace.
 */
var gweb = gweb || {};


/**
 * Initializes gweb.analytics namespace.
 */
gweb.analytics = gweb.analytics || {};


/**
 * Initializes an object that supports running an experiment on a web page.
 * @param {object} config An object with configuration parameters.
 * @constructor
 */
gweb.analytics.experiment = function(config) {

  // Set up the required configuration variables.
  this.analytics_tracker = config.analytics_tracker;
  this.frequency_to_run = config.frequency_to_run || 1;
  this.experiment_id = config.experiment_id;
  this.variations = config.variations;
  this.active_variation_id = null;
  this.use_cookies = config.use_cookies;
  this.debug_enabled = config.debug_enabled || false;
  this.debug_textarea_id = config.debug_textarea_id;

  this.initHook = config.initHook;
  this.convertHook = config.convertHook;

  this.running_experiment = false;
  this.experiment_nodes = [];
  this.analytics_var = 'gweb-experiment-' + this.experiment_id + ' ';

  // Make sure we have a Google Analytics tracker.
  try {
    this.gaTracker = this.analytics_tracker;
  } catch (err) {
    this.gaTracker = null;
    this.running_experiment = false;
    this.logDebug('Couldn\'t find a Google Analytics tracker.');
  }

  /**
   * Determine if I should run an experiment based on the hash or the cookie.
   * this.runExperimentOrNot() invokes this.pickExistingVariation() which sets
   * the active variation ID.
   */
  if (this.runExperimentOrNot() && this.gaTracker) {
    this.logDebug('Active variation: ' + this.active_variation_id);
    this.running_experiment = true;
    this.showVariationNodes();
    this.setAnalyticsVar();
    this.trackExperimentView();
    this.setVariation();
  } else {
    this.logDebug('Not running experiment.');
    return;
  }

};


/**
 * Logs a debug message if debug is enabled, either to the console or to a
 * textarea present on the page.
 * @param {string} log_message The message to log.
 */
gweb.analytics.experiment.prototype.logDebug = function(log_message) {
  if (this.debug_enabled) {
    console.log(log_message);
    this.debug_textarea_el = document.getElementById(this.debug_textarea_id);
    if (this.debug_textarea_el) {
      this.debug_textarea_el.value += '* ' + log_message + '\n';
      this.debug_textarea_el.scrollTop = this.debug_textarea_el.scrollHeight;
    }
  }
};


/**
 * Invokes the _setVar method of the Analytics tracker.
 */
gweb.analytics.experiment.prototype.setAnalyticsVar = function() {
  this.analytics_var += 'gweb-variation-' + this.active_variation_id;
  this.logDebug('Setting Analytics variable: ' + this.analytics_var);
  this.gaTracker._setVar(this.analytics_var);
};


/**
 * Shows the nodes for the active variation.
 */
gweb.analytics.experiment.prototype.showVariationNodes = function() {

  this.experiment_nodes = this.getElementsByClassName('gwebx-variation',
      document.body);
  for (var node in this.experiment_nodes) {
    if (this.experiment_nodes[node].className.indexOf('gwebx-variation-' +
        this.active_variation_id) == -1) {
      this.hide(this.experiment_nodes[node]);
    } else {
      this.show(this.experiment_nodes[node]);
    }
  }

};


/**
 * Gets HTML elements that match with class name. TODO: Replace with Gweb
 * version when it is released.
 * @param {string} classname The name of the class to match.
 * @param {object} node The parent node to search under.
 * @return {array} A list of matching HTML elements.
 */
gweb.analytics.experiment.prototype.getElementsByClassName =
    function(classname, node) {
  var matchedResults = [];
  var re = new RegExp('\\b' + classname + '\\b');
  var elementsWithTag = node.getElementsByTagName('*');
  for (var i = 0, j = elementsWithTag.length; i < j; i++) {
    if (re.test(elementsWithTag[i].className)) {
      matchedResults.push(elementsWithTag[i]);
    }
  }
  return matchedResults;
};


/**
 * Shows an element.
 * @param {object} element The HTML element to show.
 */
gweb.analytics.experiment.prototype.show = function(element) {
  element.style.display = 'block';
};


/**
 * Hides an element.
 * @param {object} element The HTML element to hide.
 */
gweb.analytics.experiment.prototype.hide = function(element) {
  element.style.display = 'none';
};


/**
 * Tries to determine if we're running an experiment or not.
 * If we are running an experiment, set the active_experiment_id property
 * of the gweb.analytics.experiment object.
 * @return {boolean} Whether or not to run the experiment.
 */
gweb.analytics.experiment.prototype.runExperimentOrNot = function() {

  /**
   * Determine if we're already in an experiment by hash or cookie.
   */
  if (this.pickExistingVariation()) {
    this.active_variation_id = this.active_variation_id;
    return true;

  /**
   * Determine if we want to run an experiment based on the frequency.
   * If so, pick a random variation to run.
   */
  } else if (this.frequency_to_run > Math.random()) {

    var i = Math.floor(this.variations.length * Math.random())

    this.active_variation_id = this.variations[i];
    return true;

  /**
   * Don't run an experiment.
   */
  } else {
    return false;
  }

};


/**
 * Determines which variation to run based on, in order: URL hash, cookie.
 * If a variation is found, it sets the active variation in the object.
 * @return {boolean} Whether or not an existing experiment exists.
 */
gweb.analytics.experiment.prototype.pickExistingVariation = function() {

  var regex = new RegExp('gwebx=' + this.experiment_id + ',([^\&]*)');

  var search = window.location.hash;
  var results = regex.exec(search);

  if (results != null) {
    var variation_id = results[1];

    this.setVariation();
    this.active_variation_id = variation_id;
    return this.active_variation_id;
  }

  if (this.use_cookies) {
    var cookie_value;
    var allcookies = document.cookie;
    var param_index = allcookies.indexOf('gwebx=');

    if (param_index != -1) {
      var value_index = param_index + 6;
      var value_end = allcookies.indexOf(';', value_index);
      if (value_end == -1) {
        value_end = allcookies.length;
      }
      cookie_value = allcookies.substring(value_index, value_end);
    }

    var regex = new RegExp(this.experiment_id + ',([^\&]*)');
    var results = regex.exec(cookie_value);

    if (results != null) {
      var variation_id = results[1];

      this.active_variation_id = variation_id;
      this.setVariation();
      return this.active_variation_id;
    }
  }

  return null;

};


/**
 * Saves the active variation in the cookie.
 */
gweb.analytics.experiment.prototype.setVariation = function() {
  if (!this.use_cookies) {
    return;
  }

  var expiry = new Date();
  var urlTruncated = location.href.substring(
      location.href.lastIndexOf('://') + 3,location.href.length);
  var cookiePath = urlTruncated.substring(urlTruncated.indexOf('/'),
      urlTruncated.lastIndexOf('/') + 1);

  expiry.setTime(expiry.getTime() + (24 * 60 * 60 * 1000));
  document.cookie = 'gwebx=' + this.experiment_id + ',' +
    this.active_variation_id + ';path=' + cookiePath + ';expires=' + expiry.toGMTString() + ';';
};


/**
 * Invokes an Analytics Event Track indicating the experiment is viewed.
 */
gweb.analytics.experiment.prototype.trackExperimentView = function() {
  if (this.running_experiment == false) {
    return;
  }

  this.gaTracker._trackEvent('Gweb Experiment: ' + this.experiment_id,
      'View: ' + this.active_variation_id);
  this.logDebug('Tracking Analytics Event: Gweb Experiment: ' +
      this.experiment_id + ', View: ' + this.active_variation_id);

};


/**
 * Invokes an Analytics Event Track.
 */
gweb.analytics.experiment.prototype.trackSuccess = function() {
  if (this.running_experiment == false) {
    return;
  }

  var label = arguments;
  if (label.length == 0) {
    this.logDebug('Successful conversion.');
    this.logDebug('Tracking Analytics Event: Gweb Experiment: ' +
        this.experiment_id + ', Success: ' + this.active_variation_id);
    this.gaTracker._trackEvent('Gweb Experiment: ' + this.experiment_id,
        'Success: ' + this.active_variation_id);
  } else {
    this.logDebug('Successful conversion with label: ' + label[0]);
    this.logDebug('Tracking Analytics Event: Gweb Experiment: ' +
        this.experiment_id + ', Success: ' + this.active_variation_id + ', ' +
        label[0]);
    this.gaTracker._trackEvent('Gweb Experiment: ' + this.experiment_id,
        'Success: ' + this.active_variation_id, label[0]);
  }
};
