/*
<DEPENDENCIES>
</DEPENDENCIES>
*/
// Logger

/**
 * Defines the Logging class. This will usually be a single global instance
 *
 * @constructor
 */
function Logger()
{
  this.nFatal = 0;
  this.nError = this.nFatal + 1;
  this.nWarning = this.nError + 1;
  this.nDebug = this.nWarning + 1;
  this.nInfo = this.nDebug + 1;
  this.nVerbose = this.nInfo + 1;
  this.nTrace = this.nVerbose + 1;
  this.nVeryVerbose = this.nTrace + 1;

  this.sDebugFrame = 'debugFrame';
  this.bActive = true;
  this.nLevel = this.nVeryVerbose;
  this.nLine = 0;
  this.sMask = '';

  this.aColor = new Array();
  this.aPrefix = new Array();
  this.aMask = new Array();

  this.aColor[this.nFatal] = '#ff0000';
  this.aColor[this.nError] = '#ff0000';
  this.aColor[this.nWarning] = '#ff6666';
  this.aColor[this.nDebug] = '#000000';
  this.aColor[this.nInfo] = '#0000ff';
  this.aColor[this.nVerbose] = '#588AB5';
  this.aColor[this.nTrace] = '#785A95';
  this.aColor[this.nVeryVerbose] = '#006600';

  this.aPrefix[this.nFatal] = 'Fatal&nbsp;&nbsp;';
  this.aPrefix[this.nError] = 'Error&nbsp;&nbsp;';
  this.aPrefix[this.nWarning] = 'Warning';
  this.aPrefix[this.nDebug] = 'Debug&nbsp;&nbsp;';
  this.aPrefix[this.nInfo] = 'Info&nbsp;&nbsp;&nbsp;';
  this.aPrefix[this.nVerbose] = '.......';
  this.aPrefix[this.nTrace] = 'Trace&nbsp;&nbsp;';
  this.aPrefix[this.nVeryVerbose] = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';

  this.aHex = new Array();
  this.aHex[0] = '0';
  this.aHex[1] = '1';
  this.aHex[2] = '2';
  this.aHex[3] = '3';
  this.aHex[4] = '4';
  this.aHex[5] = '5';
  this.aHex[6] = '6';
  this.aHex[7] = '7';
  this.aHex[8] = '8';
  this.aHex[9] = '9';
  this.aHex[10] = 'a';
  this.aHex[11] = 'b';
  this.aHex[12] = 'c';
  this.aHex[13] = 'd';
  this.aHex[14] = 'e';
  this.aHex[15] = 'f';

  this.aModuleColor = new Array();

  this.lastLine = null;
  // for scrolling


  try
  {
    if( !parent.frames[this.sDebugFrame] )
      this.bActive = false;
  }
  catch( e )
  {
    this.bActive = false;
  }

  if( this.bActive && parent.frames[this.sDebugFrame] )
  {
    parent.frames[this.sDebugFrame].document.write('<html><head>\n');
    parent.frames[this.sDebugFrame].document.write('<link rel="stylesheet" href="../../af-web/js/Logger.css" type="text/css">\n');
    parent.frames[this.sDebugFrame].document.write('</head><body>\n');
    parent.frames[this.sDebugFrame].document.write('<p class="Logger_Text">\n');
  }

  if( this.bActive && parent.frames[this.sDebugFrame] )
  {
    for( var i = this.nFatal; i <= this.nVeryVerbose; i++ )
    {
      this.aMask[i] = true;
    }
  }

  if( this.bActive && parent.frames[this.sDebugFrame] )
  {
    var sQuery = parent.document.location.search;
    sQuery = sQuery.substring(1, sQuery.length);
    var aArgs = sQuery.match(/([^=]+)\=([^&]+)/);

    if( aArgs != null )
    {
      for( var i = 1; i < aArgs.length; i++ )
      {
        if( false )
        {
        }
        else if( aArgs[i] == 'logmask' )
        {
          i++;

          this.setLogMask(aArgs[i]);
        }
      }
    }
  }

}


/**
 * Set log mask from string. 
 *
 * @param sString mask string like "f,e,d,i,v,t,vv" or "-t,vv"
 */
Logger.prototype.setLogMask = function( sMask )
{
  var aName = new Array();
  aName[this.nFatal] = 'f';
  aName[this.nError] = 'e';
  aName[this.nWarning] = 'w';
  aName[this.nDebug] = 'd';
  aName[this.nInfo] = 'i';
  aName[this.nVerbose] = 'v';
  aName[this.nTrace] = 't';
  aName[this.nVeryVerbose] = 'vv';

  this.sMask = sMask;

  for( var i = this.nFatal; i <= this.nVeryVerbose; i++ )
  {
    this.aMask[i] = true;
  }

  if( sMask == '' )
  {
    //
  }
  else
  {
    var bValue = true;

    if( false )
    {
    }
    else if( sMask.substring(0, 1) == '-' )
    {
      sMask = sMask.substring(1, sMask.length);
      bValue = false;
      for( var j = this.nFatal; j <= this.nVeryVerbose; j++ ) this.aMask[j] = true;
    }
    else
    {
      bValue = true;
      for( var j = this.nFatal; j <= this.nVeryVerbose; j++ ) this.aMask[j] = false;
    }

    aLogmask = sMask.split(',');
    for( var j = 0; j < aLogmask.length; j++ )
    {
      var nLevel = -1;
      for( var k = this.nFatal; k <= this.nVeryVerbose; k++ )
      {
        if( aLogmask[j] == aName[k] )
        {
          nLevel = k;
          break;
        }
      }
      if( nLevel >= 0 )
      {
        this.aMask[nLevel] = bValue;
      }
    }
  }

}


/**
 * Return the log mask as string. 
 *
 */
Logger.prototype.getLogMask = function()
{
  var aName = new Array();
  aName[this.nFatal] = 'f';
  aName[this.nError] = 'e';
  aName[this.nWarning] = 'w';
  aName[this.nDebug] = 'd';
  aName[this.nInfo] = 'i';
  aName[this.nVerbose] = 'v';
  aName[this.nTrace] = 't';
  aName[this.nVeryVerbose] = 'vv';

  var sMask = '';

  for( var i = this.nFatal; i <= this.nVeryVerbose; i++ )
  {
    if( this.aMask[i] )
    {
      if( sMask != '' ) sMask += ',';
      sMask += aName[i];
    }
  }

  return sMask;
}


Logger.prototype.getHex = function( nValue )
{
  var sValue = '';
  var nLow = nValue % 16;
  var nHigh = (nValue - nValue % 16) / 16;
  sValue = this.aHex[nHigh] + this.aHex[nLow];
  //alert(nValue+' '+nLow+' '+nHigh+' '+sValue);
  return sValue;
}

Logger.prototype.getModuleColor = function( sModule )
{
  if( this.aModuleColor[sModule] == null )
  {
    var sColor = '#000000';
    var nSum = 0;
    for( var i = 0; i < sModule.length; i++ )
    {
      var nChar = sModule.charCodeAt(i);
      nSum += parseInt(nChar);
    }
    var nRed = nSum % 171;
    var nGreen = nSum % 215;
    var nBlue = nSum % 191;
    this.aModuleColor[sModule] = '#' + this.getHex(nRed) + this.getHex(nGreen) + this.getHex(nBlue);
  }
  return this.aModuleColor[sModule];
}

/**
 * Generic print. 
 *
 * @param nLevel The debug level
 * @param sModule module name or class name that reports
 * @param sMessage log message, usually contains current METHOD and the actual message text
 */
Logger.prototype.print = function( nLevel, sModule, sMessage )
{
  if( !this.bActive || !parent.frames[this.sDebugFrame] ) return;

  if( nLevel <= this.nLevel && this.aMask[nLevel] )
  {
    this.nLine++;
    var sColor = this.aColor[nLevel];

    var bBold = false;
    switch( nLevel )
            {
      case this.nFatal: bBold = true; break;
      case this.nError: bBold = true; break;
      //case this.nDebug: bBold = true; break;
      default: ;
    }

    if( !bBold ) sColor = this.getModuleColor(sModule);

    switch( sModule )
            {
      case 'bla': sColor = '#123456'; break;
      default: ;
    }

    if( nLevel == this.nDebug )
    {
      sColor = '#0000ff';
    }

    var sOut = '<font id="Logger_' + (this.nLine) + '" color=\"' + sColor + '\">' + this.aPrefix[nLevel] + (bBold?'<b>'
            :'') + ' [' + sModule + '] ' + sMessage + (bBold?'</b>':'') + '</font><br>\n';
    parent.frames[this.sDebugFrame].document.write(sOut);

    var o = parent.frames[this.sDebugFrame].document.getElementById('Logger_' + this.nLine);
    if( o != null )
    {
      if( this.nLine % 20 == 0 )
      {
        o.scrollIntoView(true);
        this.lastLine = null;
      }
      else
      {
        this.lastLine = o;
      }
    }
  }

}


/**
 * Wrapper for fatal error messages. Fatal means program will not be able to continue.
 *
 * @param sModule see Logger.prototype.print
 * @param sMessage see Logger.prototype.print
 */
Logger.prototype.fatal = function( sModule, sMessage )
{
  if( !this.bActive || !parent.frames[this.sDebugFrame] ) return;
  this.print(this.nFatal, sModule, sMessage);
}



/**
 * Wrapper for error messages. Means something serious happens, but program will recover
 *
 * @param sModule see Logger.prototype.print
 * @param sMessage see Logger.prototype.print
 */
Logger.prototype.error = function( sModule, sMessage )
{
  if( !this.bActive || !parent.frames[this.sDebugFrame] ) return;
  this.print(this.nError, sModule, sMessage);
}


/**
 * Wrapper for warnings. A bad condition.
 *
 * @param sModule see Logger.prototype.print
 * @param sMessage see Logger.prototype.print
 */
Logger.prototype.warning = function( sModule, sMessage )
{
  if( !this.bActive || !parent.frames[this.sDebugFrame] ) return;
  this.print(this.nWarning, sModule, sMessage);
}


/**
 * Wrapper for debug output. Use this for temporary printf-debugging.
 *
 * @param sModule see Logger.prototype.print
 * @param sMessage see Logger.prototype.print
 */
Logger.prototype.debug = function( sModule, sMessage )
{
  if( !this.bActive || !parent.frames[this.sDebugFrame] ) return;
  this.print(this.nDebug, sModule, sMessage);
}


/**
 * Wrapper for info messages. Output important state changes. Expected to generate few lines per second
 *
 * @param sModule see Logger.prototype.print
 * @param sMessage see Logger.prototype.print
 */
Logger.prototype.info = function( sModule, sMessage )
{
  if( !this.bActive || !parent.frames[this.sDebugFrame] ) return;
  this.print(this.nInfo, sModule, sMessage);
}


/**
 * Wrapper for verbose output. Expected to generate 50 lines per second.
 *
 * @param sModule see Logger.prototype.print
 * @param sMessage see Logger.prototype.print
 */
Logger.prototype.verbose = function( sModule, sMessage )
{
  if( !this.bActive || !parent.frames[this.sDebugFrame] ) return;
  this.print(this.nVerbose, sModule, sMessage);
}


/**
 * Wrapper for method entry messages, optional exit messages.
 *
 * @param sModule see Logger.prototype.print
 * @param sMessage see Logger.prototype.print
 */
Logger.prototype.trace = function( sModule, sMessage )
{
  if( !this.bActive || !parent.frames[this.sDebugFrame] ) return;
  this.print(this.nTrace, sModule, sMessage);
}


/**
 * Wrapper for very verbose output. Expected to generate 1000 lines per second.
 *
 * @param sModule see Logger.prototype.print
 * @param sMessage see Logger.prototype.print
 */
Logger.prototype.veryverbose = function( sModule, sMessage )
{
  if( !this.bActive || !parent.frames[this.sDebugFrame] ) return;
  this.print(this.nVeryVerbose, sModule, sMessage);
}


Logger.prototype.scrollToLastLine = function()
{
  if( this.lastLine != null )
  {
    this.lastLine.scrollIntoView(true);
    this.lastLine = null;
  }
}

Logger.prototype.objectTypeToString = function( object )
{
  if( object == undefined )
    return 'object is UNDEFINED';
  if( object == null )
    return 'object is NULL';

  return object.constructor;
}

Logger.prototype.objectToString = function( object, mode )
{
  if( object == undefined )
    return 'UNDEFINED';
  if( object == null )
    return 'NULL';
  // ---- doesn't work in IE.
  //  if( HTMLElement.prototype.isPrototypeOf(object) )
  //    return this.htmlElementToString(object);
  // ----
  if( object.tagName != undefined )
    return this.htmlElementToString(object);

  var result = '';

  if( mode == undefined || mode == null )
  {
    for( name in object )
    {
      if( result == '' )
        result += name;
      else
        result += ', ' + name;
    }

    if( result == '' )
      result = '{ -- NO ENUMERABLE PROPERTIES -- }';
    else
      result = '{ ' + result + ' }';
  }
  else if( mode == 'v' )
  {
    for( name in object )
    {
      result += name + '=' + object[name] + ';';
    }

    if( result == '' )
      result = '{ -- NO ENUMERABLE PROPERTIES -- }';
    else
      result = '{ ' + result + ' }';
  }
  else if( mode == 'vv' )
  {
    for( name in object )
    {
      result += name + '=' + this.objectToString(object[name], 'v') + ';';
    }

    if( result == '' )
      result = '{ -- NO ENUMERABLE PROPERTIES -- }';
    else
      result = '{ ' + result + ' }';
  }

  return result;
}

Logger.prototype.htmlElementToString = function( object )
{
  if( object == undefined )
    return 'UNDEFINED';
  if( object == null )
    return 'NULL';

  return '{ tagName=' + object.tagName
          + (object.id == undefined ? '' : (object.id == null ? '' : '; id=' + object.id))
          + (object.className == undefined ? '' : (object.className == null ? '' : '; className=' + object.className))
          + ' }';
}


// Greate a static instance
Log = new Logger();
setInterval('Log.scrollToLastLine()', 1000);

// Check the logger
Log.info('main', 'Logger starting with mask=' + Log.getLogMask());




