/*******************************************************************************
 * Copyright 2015 IBM Corp.
 *
 * 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.
 *******************************************************************************/
'use strict';
var Probe = require('../lib/probe.js');
var aspect = require('../lib/aspect.js');
var request = require('../lib/request.js');
var util = require('util');
var am = require('../');

/**
 * Probe to instrument the MQLight npm client
 */
function MQLightProbe() {
  Probe.call(this, 'mqlight');
}
util.inherits(MQLightProbe, Probe);

MQLightProbe.prototype.attach = function(name, target) {
  var that = this;
  if (name != 'mqlight') return target;
  if (target.__probeAttached__) return;
  target.__probeAttached__ = true;

  // After 'createClient'
  aspect.after(target, 'createClient', {}, function(target, methodName, createClientArgs, context, rc) {
    var thisClient = rc; // the MQLight client that was created
    // Just monitor 'send' for now as not sure what else will be useful
    var methods = 'send';
    // Before one of the above methods is called on the client
    aspect.around(
      thisClient,
      methods,
      function(target, methodName, args, probeData) {
        // Start the monitoring for the method
        that.metricsProbeStart(probeData, methodName, args);
        that.requestProbeStart(probeData, methodName, args);
        // Advise the callback for the method.  Will do nothing if no callback is registered
        aspect.aroundCallback(args, probeData, function(target, callbackArgs, probeData) {
          // method has completed and the callback has been called, so end the monitoring
          var callbackPosition = aspect.findCallbackArg(args);
          that.metricsProbeEnd(probeData, methodName, args, thisClient);
          that.requestProbeEnd(probeData, methodName, args, thisClient);
        });
      },
      function(target, methodName, args, probeData, rc) {
        // If no callback used then end the monitoring after returning from the method instead
        if (aspect.findCallbackArg(args) == undefined) {
          that.metricsProbeEnd(probeData, methodName, args, thisClient);
          that.requestProbeEnd(probeData, methodName, args, thisClient);
        }
        return rc;
      }
    );

    // Advise the callback code that is called when a message is received
    aspect.before(thisClient, 'on', function(target, methodName, args, probeData) {
      // only care about 'message' events
      if (args[0] == 'message') {
        // Must be a callback so no need to check for it
        aspect.aroundCallback(
          args,
          {},
          function(obj, callbackArgs, probeData) {
            that.metricsProbeStart(probeData, 'message', callbackArgs);
            that.requestProbeStart(probeData, 'message', callbackArgs);
          },
          function(target, callbackArgs, probeData, ret) {
            // method has completed and the callback has been called, so end the monitoring

            that.metricsProbeEnd(probeData, 'message', callbackArgs, thisClient);
            that.requestProbeEnd(probeData, 'message', callbackArgs, thisClient);
            return ret;
          }
        );
      }
    });
    return rc;
  });
  return target;
};

/*
 * Lightweight metrics probe end for MQLight messages
 */
MQLightProbe.prototype.metricsEnd = function(probeData, method, methodArgs, client) {
  if (probeData && probeData.timer) {
    probeData.timer.stop();
    /* eslint no-redeclare:0 */
    if (method == 'message') {
      var data = methodArgs[0];
      if (data.length > 25) {
        data = data.substring(0, 22) + '...';
      }
      var topic = methodArgs[1].message.topic;
      am.emit('mqlight', {
        time: probeData.timer.startTimeMillis,
        clientid: client.id,
        data: data,
        method: method,
        topic: topic,
        duration: probeData.timer.timeDelta,
      });
    } else if (method == 'send') {
      var data = methodArgs[1];
      if (data.length > 25) {
        data = data.substring(0, 22) + '...';
      }
      var qos;
      var options; // options are optional - check number of arguments.
      if (methodArgs.length > 3) {
        options = methodArgs[2];
        qos = options[0];
      }
      am.emit('mqlight', {
        time: probeData.timer.startTimeMillis,
        clientid: client.id,
        data: data,
        method: method,
        topic: methodArgs[0],
        qos: qos,
        duration: probeData.timer.timeDelta,
      });
    }
  }
};

/*
 * Heavyweight request probes for MQLight messages
 */
MQLightProbe.prototype.requestStart = function(probeData, method, methodArgs) {
  if (method == 'message') {
    probeData.req = request.startRequest('mqlight', method, true, probeData.timer);
  } else {
    probeData.req = request.startRequest('mqlight', method, false, probeData.timer);
  }
};

MQLightProbe.prototype.requestEnd = function(probeData, method, methodArgs, client) {
  if (probeData && probeData.req) {
    if (method == 'message') {
      var data = methodArgs[0];
      if (data.length > 25) {
        data = data.substring(0, 22) + '...';
      }
      probeData.req.stop({
        clientid: client.id,
        data: data,
        method: method,
        topic: methodArgs[0],
      });
    } else if (method == 'send') {
      var data = methodArgs[1];
      if (data.length > 25) {
        data = data.substring(0, 22) + '...';
      }
      var qos;
      var options; // options are optional - check number of arguments.
      if (methodArgs.length > 3) {
        options = methodArgs[2];
        qos = options[0];
      }
      probeData.req.stop({
        clientid: client.id,
        data: data,
        method: method,
        topic: methodArgs[0],
        qos: qos,
      });
    }
  }
};

module.exports = MQLightProbe;
