/*
 * Copyright © 2012, 2013 Pedro Agullo Soliveres.
 * 
 * This file is part of Log4js-ext.
 *
 * Log4js-ext is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License.
 *
 * Commercial use is permitted to the extent that the code/component(s)
 * do NOT become part of another Open Source or Commercially developed
 * licensed development library or toolkit without explicit permission.
 *
 * Log4js-ext is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Log4js-ext.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * This software uses the ExtJs library (http://extjs.com), which is 
 * distributed under the GPL v3 license (see http://extjs.com/license).
 */

/*jslint strict:false */

(function() {
   // "use strict"; //$NON-NLS-1$

   /** 
    * An appender that knows how to output data to the built-in log viewer
    * window (see {@link Sm.log.LogViewerWindow}).
    */
   Ext.define('Sm.log.LogViewerAppender', { //$NON-NLS-1$
      extend : 'Sm.log.AppenderBase',
      
      uses : ['Sm.log.util.Assert'],
      
      /**
       * @private
       * 
       * Attaches the appender to a viewer.
       * 
       * @param {Sm.log.LogViewerWindow} viewer 
       *     The viewer to which to attach this appender.
       * 
       * @returns {void}
       */
      attachViewer : function( viewer ) {
         Sm.log.util.Assert.assert(viewer);
         Sm.log.util.Assert.assert( !this.viewer );
         this.viewer = viewer;
      },
      
      /**
       * @private
       * 
       * Detaches the appender from this viewer.
       * 
       * @returns {void}
       */
      detachViewer : function() {
         Sm.log.util.Assert.assert( this.viewer);
         if( this.isLogging() ) {
            this.startBuffering();
         }
         this.viewer = null;         
      },
      
      /**
       * Starts buffering logs.
       * 
       * While buffering, logs are not sent to the viewer window automatically,
       * but rather they are kept apart until we change the status to logging.
       * Then, all buffered logs will be sent to the window.
       * 
       * This is useful in cases where the viewer window should not be updated
       * to avoid interference while debugging, or where the window simply does
       * not exist but we want to register such log nevertheless.
       * 
       * @returns {void}
       */
      startBuffering : function() {
         this.state = Sm.log.AppenderBase.BUFFERING;
      },
      
      /**
       * Returns true if the viewer is buffering logs.
       * 
       * @returns {Boolean} true if the viewer is buffering logs.
       */
      isBuffering : function () {
         return this.state === Sm.log.AppenderBase.BUFFERING;
      },
      
      /**
       * Clears the buffered logs.
       * 
       * The buffered log are lost.
       * 
       * @returns {void}
       */
      clearBuffer : function() {
         this.buffer = [];
      },
      
      /**
       * Returns true if we can log.
       * 
       * Note that we might not be able to log directly when the viewer
       * window is not available, for example.
       * 
       * @returns {Boolean}
       */
      canLog : function() {
         return this.viewer;
      },

      /**
       * Sets the appender in the loggins state.
       * 
       * All logs sent to the appender from now on will be logged.
       * Besides, if there were some buffered logs, they will be immediately
       * sent to the log viewer window, if any.
       * 
       * @returns {void}
       */
      startLogging : function() {
         // If not attached, we can't start logging
         Sm.log.util.Assert.assert( this.canLog() );
         
         if( this.buffer.length > 0 ) {
            this.doPhysicalLog( this.buffer);
            this.clearBuffer();
         }
         this.callParent(arguments);
      },
      
      /**
       * @inheritDoc
       */
      stopLogging : function () {
         this.callParent(arguments);

         this.clearBuffer();
      },
      
      /** 
       * Returns true, as this appender supports log buffering.
       * 
       * @returns {Boolean} true, as this appender supports log buffering.
       */
      supportsBuffering : function() {
         return true;
      },
      
      /**
       * @protected
       * @inheritDoc
       */
      setInitialState : function () {
         this.startBuffering();
      },
      
      /**
       * Creates a new log viewer appender.
       * 
       * @param cfg
       */
      constructor : function (cfg ) {
         this.callParent(arguments);
         
         // We are going to output to HTML, in the end
         this.setHtmlEncodedLogEventProperties( 
            [// "time" // Never encode this, it is a Date
             // ,"formattedTime" // If some layout adds HTML, we want HTML 
             "message"
             ,"ndc"
             // ,"hasLoggedObject" // Never encode this, it is a boolean
             ,"formattedMessage" 
             // ,"formattedLoggedObject" // We *are* adding HTML, want it used
             ] );
         this.clearBuffer();
      },

      /**
       * @inheritDoc
       * 
       * @protected
       * 
       * @param logEvent
       */
      doLog : function(logEvent) {
         var indent = 5;
         
         // @todo: pag Dislike doing this here, maybe should create a custom
         // layout that adds the formattedMultilineLoggedObject?
         // But that is cumbersome
         logEvent.formattedMultilineLoggedObject = '';
         if( logEvent.hasLoggedObject) {
           logEvent.formattedMultilineLoggedObject = 
              this.getLayout().jsonLikeFormat( 
                       logEvent.loggedObject, true, true, indent );
         }
         logEvent.formattedMultilineMessage = 
            logEvent.formattedMessage.replace( /(\r\n|\n|\r)/gm, '<br>');
         


         if( this.isBuffering()) {
            this.buffer.push(logEvent);
         }
         else {
            this.doPhysicalLog([logEvent]);
         }
      },

      /**
       * @private
       * 
       * Performs outputs of an array of log events.
       * 
       * Using an array can help with performance, as it allows to
       * send a handful of buffered logs in a single step.
       * 
       * @param {Array} logEvents
       * 
       * @returns {void}
       */
      doPhysicalLog : function(logEvents) {
         Sm.log.util.Assert.assert( this.viewer);
         this.viewer.appendLoggingEvents(logEvents);
      }
      
   });
}());