'use strict';


const { deepMerge } = require('../utilities');
const { EngineError } = require('../error');
const { report } = require('./logger');
const { getRequestInfo } = require('./request_info');
const { getRequestMessage } = require('./request_message');


// We use symbols to make those members private
const reportSym = Symbol('report');

class Log {

  constructor({ opts: { logger, loggerLevel, loggerFilter }, phase }) {
    this._info = {};
    this._isBuffered = false;
    this._buffer = [];
    Object.assign(this, { logger, loggerLevel, loggerFilter, phase });
  }

  add(obj) {
    this._info = deepMerge(this._info, obj);
  }

  info(...args) {
    this[reportSym]('info', ...args);
  }

  log(...args) {
    this[reportSym]('log', ...args);
  }

  warn(...args) {
    this[reportSym]('warn', ...args);
  }

  error(...args) {
    this[reportSym]('error', ...args);
  }

  [reportSym](level, rawMessage = '', logObj = {}){
    if (typeof rawMessage !== 'string') {
      const message = `Message must be a string: '${rawMessage}'`;
      throw new EngineError(message, { reason: 'UTILITY_ERROR' });
    }
    if (logObj == null || logObj.constructor !== Object) {
      const message = `Log object must be an object: '${JSON.stringify(logObj)}'`;
      throw new EngineError(message, { reason: 'UTILITY_ERROR' });
    }

    if (this._isBuffered) {
      this._buffer.push({ level, rawMessage, logObj });
      return;
    }

    if (this.phase === 'request') {
      logObj.requestInfo = getRequestInfo(this._info, this.loggerFilter);
      if (logObj.type === 'call') {
        rawMessage = getRequestMessage(logObj.requestInfo);
      }
    }

    logObj.phase = this.phase;

    report(this.logger, this.loggerLevel, level, rawMessage, logObj);
  }

  // Buffer log calls
  // E.g. used in requests when requestInfo is not completely built yet
  _setBuffered(isBuffered) {
    this._isBuffered = Boolean(isBuffered);

    const hasBufferedCalls = this._buffer.length > 0;
    if (!isBuffered && hasBufferedCalls) {
      this._releaseBuffer();
    }
  }

  // Release buffered log calls
  _releaseBuffer() {
    for (const { level, rawMessage, logObj } of this._buffer) {
      this[reportSym](level, rawMessage, logObj);
    }
    this._buffer = [];
  }

}


module.exports = {
  Log,
};

@@ -26,16 +26,24 @@
 eepMerge
+, buffer
  %7D = req
@@ -60,24 +60,24 @@
 tilities');%0A
-
 const %7B Engi
@@ -456,24 +456,25 @@
 o = %7B%7D;%0A
+%0A
     this
 ._isBuff
@@ -469,53 +469,54 @@
 this
-._isBuffered = false;%0A    this._buffer = %5B%5D
+%5BreportSym%5D = buffer(this%5BreportSym%5D, this)
 ;%0A
+%0A
     
@@ -1348,113 +1348,8 @@
  %7D%0A%0A
-    if (this._isBuffered) %7B%0A      this._buffer.push(%7B level, rawMessage, logObj %7D);%0A      return;%0A    %7D%0A%0A
     
@@ -1803,380 +1803,84 @@
     
-this._isBuffered = Boolean(isBuffered);%0A%0A    const hasBufferedCalls = this._buffer.length %3E 0;%0A    if (!isBuffered && hasBufferedCalls) %7B%0A      this._releaseBuffer();%0A    %7D%0A  %7D%0A%0A  // Release buffered log calls%0A  _releaseBuffer() %7B%0A    for (const %7B level, rawMessage, logObj %7D of this._buffer) %7B%0A      this%5BreportSym%5D(level, rawMessage, logObj);%0A    %7D%0A    this._buffer = %5B%5D
+const method = isBuffered ? 'cork' : 'uncork';%0A    this%5BreportSym%5D%5Bmethod%5D()
 ;%0A  

Use buffering utility in logging