// Copyright 2007 Google Inc.
// All Rights Reserved.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Defines the code for the application logging console.
 */

/**
 * The namespace we're using for all javascript classes and functions related
 * to the logging console.
 */
var AH = {};


/**
 * A collection of utility functions for reading and writing cookies.
 * @constructor
 */
AH.CookieUtil = function() {};


/**
 * Creates and adds a cookie with the specified expiration.
 * @param {String} name  The name of the desired cookie.
 * @param {String} value  The value of the desired cookie.
 * @param {Number} opt_days  If non-negative, the expiration time in days of the
 *     desired cookie. If not provided, the default value is 1.
 */
AH.CookieUtil.prototype.setCookie = function(name, value, opt_days) {
  if (opt_days == null) {
    opt_days = 1;
  }

  var expires = '';
  if (opt_days < 0) {
    var date = new Date;
    date.setTime(date.getTime() + (opt_days * 24 * 60 * 60 * 1000));
    expires = '; expires=' + date.toGMTString();
  }
  document.cookie = name + '=' + value + expires + '; path=/';
};


/**
 * Returns the value of the requested cookie if it is available, and otherwise
 * returns a default value.
 * @param {String} name  The name of the requested cookie.
 * @param {String} defaultValue  The value to return if the requested cookie
 *     cannot be found.
 * @return {String} The requested cookie's value, or the default value.
 */
AH.CookieUtil.prototype.getCookie = function(name, defaultValue) {
  var nameEQ = name + '=';
  var cookiePieces = document.cookie.split(';');
  for (var i = 0; i < cookiePieces.length; i++) {
    var c = cookiePieces[i];
    c = c.replace(/^\s+/, '');
    if (c.indexOf(nameEQ) == 0) {
      return c.substring(nameEQ.length, c.length);
    }
  }
  return defaultValue;
};


/**
 * Deletes the specified cookie.
 * @param {String} name  The name of the specified cookie.
 */
AH.CookieUtil.prototype.removeCookie = function(name) {
  this.setCookie(name, '', -100);
};


/**
 * The logging console is a div that displays log statements generated by the
 * application during its execution. It can be moved around, and the verbosity
 * can be adjusted.
 * @constructor
 */
AH.LoggingConsole = function() {
  this.baseDiv = document.getElementById('_ah_base');
  this.logSeverityLevels = ['debug', 'info', 'warning', 'error', 'critical'];
  this.cookieUtil = new AH.CookieUtil;
};


/**
 * Creates and positions the logging console based on preferences available from
 * the cookies '_ah_severity' and '_ah_position'.
 */
AH.LoggingConsole.prototype.initConsole = function() {
  // Define the font colors for the different log severity levels.
  this.addCssRule('._ah_logline_debug_prefix', 'color:#110000');
  this.addCssRule('._ah_logline_info_prefix', 'color:#440000');
  this.addCssRule('._ah_logline_warning_prefix', 'color:#880000');
  this.addCssRule('._ah_logline_error_prefix', 'color:#CC0000');
  this.addCssRule('._ah_logline_critical_prefix', 'color:#FF0000');

  // Change to the severity level stored in the cookie, defaulting to the lowest
  // severity level.
  this.changeLogSeverity(this.cookieUtil.getCookie('_ah_severity', 'debug'));

  // Move the console to position stored in the cookie, defaulting to the
  // bottom right position.
  this.moveBaseDiv(this.cookieUtil.getCookie('_ah_position', 'down_right'));
};


/**
 * Add CSS rules to the document to modify the presentation for elements of the
 * specified class name. This works for IE and Firefox, but does not work for
 * Safari.
 * @param {String} selector  A selector for the style class rule to modify.
 * @param {String} styleRules  The rules to add to the specified style class.
 */
AH.LoggingConsole.prototype.addCssRule = function(selector, styleRules) {
  // If no sheet exists for the document, create one.
  var sheet;
  if (document.createStyleSheet) {
    // For IE:
    sheet = document.createStyleSheet();
  } else {
    // For Firefox:
    var styleElement = document.createElement('style');
    document.getElementsByTagName('head')[0].appendChild(styleElement);
    sheet = (styleElement.styleSheet ?
             styleElement.styleSheet :
             styleElement.sheet);
  }

  // Add the new style rules to the style sheet.
  if (sheet.addRule) {
    // For IE:
    sheet.addRule(selector, styleRules);
  } else if (sheet.insertRule) {
    // For Firefox:
    sheet.insertRule(selector + ' { ' + styleRules + ' }',
                     sheet.cssRules.length);
  }
};


/**
 * Change the log severity level, and persist the change to the '_ah_severity'
 * cookie.
 * @param {String} newSeverity  The desired log severity level.
 */
AH.LoggingConsole.prototype.changeLogSeverity = function(newSeverity) {
  // First, find the numeric level for the provided severity.
  var severityLevel = -1;
  for (var i = 0; i < this.logSeverityLevels.length; i++) {
    if (newSeverity == this.logSeverityLevels[i]) {
      severityLevel = i;
    }
  }

  // An unknown logging severity was provided, so ignore the call.
  if (severityLevel == -1) {
    return;
  }

  // Display log lines if they have severity greater than or equal to the
  // desired severity.
  for (var i = 0; i < this.logSeverityLevels.length; i++) {
    var selector =
        '._ah_logline_' + this.logSeverityLevels[i];
    if (i < severityLevel) {
      this.addCssRule(selector, 'display:none');
    } else {
      this.addCssRule(selector, 'display:block');
    }
  }

  // Update the link text colors for the severity controls, and blur the links.
  for (var i = 0; i < this.logSeverityLevels.length; i++) {
    var linkToUpdate = document.getElementById(
        '_ah_show_' + this.logSeverityLevels[i]);
    if (i == severityLevel) {
      linkToUpdate.style.color = 'red';
    } else {
      linkToUpdate.style.color = 'blue';
    }
    linkToUpdate.blur();
  }

  // Save the new severity level to a cookie.
  this.cookieUtil.setCookie('_ah_severity', newSeverity);
};


/**
 * Set the colors for the whole navigation table to white.
 */
AH.LoggingConsole.prototype.clearNavigationTable = function() {
  document.getElementById('_ah_up_left').style.backgroundColor = 'white';
  document.getElementById('_ah_up_right').style.backgroundColor = 'white';
  document.getElementById('_ah_down_left').style.backgroundColor = 'white';
  document.getElementById('_ah_down_right').style.backgroundColor = 'white';
};


/**
 * Moves the logging console to the desired position.
 * @param {String} newPosition  The desired position, which must be one of
 *     'up_left', 'up_right', 'down_left', or 'down_right'.
 */
AH.LoggingConsole.prototype.moveBaseDiv = function(newPosition) {
  // Move the logging console to the desired position on the page.
  var newPositionPieces = newPosition.split('_');
  var newVerticalPosition = newPositionPieces[0];
  var newHorizontalPosition = newPositionPieces[1];
  if (newVerticalPosition == 'up') {
    this.baseDiv.style.top = '10px';
    this.baseDiv.style.bottom = '';
  } else {
    this.baseDiv.style.top = '';
    this.baseDiv.style.bottom = '10px';
  }
  if (newHorizontalPosition == 'left') {
    this.baseDiv.style.left = '10px';
    this.baseDiv.style.right = '';
  } else {
    this.baseDiv.style.left = '';
    this.baseDiv.style.right = '10px';
  }

  // Update the navigation table cell colors to reflect the new position.
  this.clearNavigationTable();
  document.getElementById('_ah_' + newPosition).style.backgroundColor = 'red';

  // Save the new position to a cookie.
  this.cookieUtil.setCookie('_ah_position', newPosition);
};


/**
 * Disable the logging console, and delete all cookies which were storing
 * logging console preferences.
 */
AH.LoggingConsole.prototype.closeEverything = function() {
  this.cookieUtil.removeCookie('_ah_severity');
  this.cookieUtil.removeCookie('_ah_position');
  this.baseDiv.style.display = 'none';
};
