/*
	Licensed Material - Property of IBM

	(C) Copyright 2016 IBM Corp.

	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 moduleName = 'callAdapter';

var errors = require('mdo-errors');
var serverSupport = require('mdo-server-support');

var strings = require('ibm-strings');
var path = require('path');
var fs = require('fs');
var Q = require('q');
var inquirer = require('inquirer');
var http = require('http');
var https = require('https');
var log = require('npmlog');
var externalizedStrings = require('./externalizedStrings');
var nopt = require('nopt');
var qs = require('querystring');

var serverInfo;

module.exports.run = function(argv) {
  log.level = global.MDO_CLI_LOG_LEVEL ? global.MDO_CLI_LOG_LEVEL : log.level;

  log.silly('call', 'enter argv: \'%j\'', argv);
  var deferred =  Q.defer();

  var opts = nopt({
    file: String,
    type: String,
    formParams: String,
    headerParams: String,
    queryParams: String,
    server: String,
    runtime: String,
    id: String,
    secret: String
  }, {
    f: '--file',
    t: '--type',
    fp: '--form',
    h: '--header',
    q: '--query',
    server: '--server',
    runtime: '--runtime',
    i: '--id',
    s: '--secret'
  }, argv, 0);

  argv = opts.argv.remain;

  if (opts.runtime && !opts.server) {
    var runtimeError = errors.getError('RUNTIMEWITHOUTSERVER');
    strings.fatal(runtimeError.description);
    deferred.reject(runtimeError);
  } else {
    serverSupport.getServerDetails(opts.server, opts.runtime, undefined, true).then(function(profile) {
      serverInfo = profile.serverProfile;
      serverInfo.id = opts.id;
      serverInfo.secret = opts.secret;

      var adapters;
      var adapterObj;

      profile.getAdapters().then(function(adaptersJson) {
        adapters = getAdaptersList(adaptersJson);

        // Direct Mode
        if (argv.length > 0) {
          var endpoint = argv.shift().split('/');
          var adapter = endpoint[0];

          if (adapters.indexOf(adapter) === -1) {
            var adapterNotFound = errors.getError('ADAPTERNOTFOUND', adapter, serverInfo.runtime);
            strings.fatal(adapterNotFound.description);
            deferred.reject(adapterNotFound);
          }

          endpoint.shift();
          endpoint = endpoint.join('/');
          endpoint = encodeURI(endpoint);

          getAdapterObject(adapter, profile).then(function(adapterObject) {
            adapterObj = adapterObject;
            return directMode(adapterObject, endpoint, argv, opts);
          })
          .then(function(parameters) {
            serverInfo.scope = getScope(adapterObj, endpoint);
            return callAdapter({ adapter : adapter,
                                 endpoint: endpoint,
                                 parameters: parameters,
                                 callType: opts.type === 'true' ? 'get' : opts.type || 'get',
                                 isJava: adapterObj.isJava });
          })
          .then(function() {
            log.silly('call', 'successfuly called adapter: ' + adapter);
            deferred.resolve();
          })
          .catch(function(error) {
            strings.fatal(error.description);
            deferred.reject(error);
          });
        }

        // Interactive Mode
        else {
          interactiveMode(adapters, profile).then(function() {
            deferred.resolve();
          }, function(error) {
            strings.fatal(error.description);
            deferred.reject(error);
          });
        }
      }, function (error) {
        strings.fatal(error.description);
        deferred.reject(error);
      }); //End of the getAdapters
    }, function (error) {
      strings.fatal(error.description);
      deferred.reject(error);
    });
  }
  return deferred.promise;
};

/*
 * Prompt the user the existing endpoints in the selected adapter
 */
function promptEndpoints(adapterObj, endpoints) {
  var deferred = Q.defer();

  var endpoint;
  var endpointParameters = [];
  inquirer.prompt([
  {
    name: 'endpoint',
    message: externalizedStrings.whatEndpoint,
    type: 'list',
    choices: endpoints,
    when: function () {
      if (endpoints.length === 1)
        return strings.format(externalizedStrings.invokingProcedure,endpoints[0].value,adapterObj.name);
      else
        return endpoints.length > 1;
    }
  }
  ], function (answers) {
    var endpointIndex;
    try {
      var endpointSplit = answers.endpoint.split(':');
      endpoint = endpointSplit[1];
      if (endpoint) {
        endpointIndex = endpointSplit[0];
        endpointParameters = endpoints[endpointIndex].endpointParameters;
      } else {
        endpoint = answers.endpoint || endpoints[0].value;
      }
    } catch(e) {
      endpoint = answers.endpoint || endpoints[0].value;
    }

    if (adapterObj.isJava) {
      promptEndpointParameters(adapterObj, endpoints, endpoint, endpointParameters, endpointIndex)
      .then(function() {
        deferred.resolve();
      })
      .catch(function(error) {
        deferred.reject(error);
      });
    } else {
      promptParameters(adapterObj, endpoints, endpoint)
      .then(function() {
        deferred.resolve();
      })
      .catch(function(error) {
        deferred.reject(error);
      });
    }
  });
  return deferred.promise;
}

/*
 * Prompt java endpoint's parameters
 */
function promptEndpointParameters(adapterObj, endpoints, endpoint, endpointParameters, endpointIndex) {
  var deferred = Q.defer();
  if (endpointParameters) {
    var getTPrompts = function (promptType) {
      if (endpointParameters.hasOwnProperty(promptType) && promptType === 'PATH') {
        return {
          name: 'pathEndpoint',
          message: strings.format(externalizedStrings.enterPathParameters,endpoint),
          type: 'input',
          when: function () {
            return endpointParameters.hasOwnProperty('PATH');
          }
        };
      } else if (endpointParameters.hasOwnProperty(promptType)) {
        var promptTypeParameters = endpointParameters[promptType];
        var promptTypePromptsTemp = [];
        for (var i in promptTypeParameters) {
          promptTypePromptsTemp.push({
            name: promptTypeParameters[i],
            message: strings.format(externalizedStrings.enterParameter,promptType,promptTypeParameters[i]),
            type: 'input'
          });
        }
        return promptTypePromptsTemp;
      } else return null;
    };

    var promptsObject = {'PATH':getTPrompts('PATH'),'FORM':getTPrompts('FORM'),'QUERY':getTPrompts('QUERY'),'HEADER':getTPrompts('HEADER')};
    var sendParameters = {};
    var index = 0;

    for(var param in promptsObject) {
      if(!promptsObject[param]) delete promptsObject[param];
    }

    // what is going on here........
    inquirer.prompt(promptsObject[Object.keys(promptsObject)[index]], function (answers) {
      sendParameters[Object.keys(promptsObject)[index]] = answers;
      index++;
      if (Object.keys(promptsObject)[index]) {
        inquirer.prompt(promptsObject[Object.keys(promptsObject)[index]], function (answers) {
          sendParameters[Object.keys(promptsObject)[index]] = answers;
          index++;
          if (Object.keys(promptsObject)[index]) {
            inquirer.prompt(promptsObject[Object.keys(promptsObject)[index]], function (answers) {
              sendParameters[Object.keys(promptsObject)[index]] = answers;
              index++;
              if (Object.keys(promptsObject)[index]) {
                inquirer.prompt(promptsObject[Object.keys(promptsObject)[index]], function (answers) {
                  sendParameters[Object.keys(promptsObject)[index]] = answers;
                  index++;
                  promptParameters(adapterObj, endpoints, endpoint, sendParameters, endpointIndex)
                  .then(function() {
                    deferred.resolve();
                  })
                  .catch(function(error) {
                    deferred.reject(error);
                  });
                });
            } else promptParameters(adapterObj, endpoints, endpoint, sendParameters, endpointIndex)
            .then(function() {
              deferred.resolve();
            })
            .catch(function(error) {
              deferred.reject(error);
            });
          });
        } else promptParameters(adapterObj, endpoints, endpoint, sendParameters, endpointIndex)
        .then(function() {
          deferred.resolve();
        })
        .catch(function(error) {
          deferred.reject(error);
        });
      });
    } else promptParameters(adapterObj, endpoints, endpoint, sendParameters, endpointIndex)
    .then(function() {
      deferred.resolve();
    })
    .catch(function(error) {
      deferred.reject(error);
    });
  });
  } else {
    promptParameters(adapterObj, endpoints, endpoint, endpoint, endpointIndex)
    .then(function() {
      deferred.resolve();
    })
    .catch(function(error) {
      deferred.reject(error);
    });
  } // end if-else endpointParameters
  return deferred.promise;
} // end promptEndpointParameters function

/*
 * Prompt the user for the parameters on the adapter endpoint
 */
function promptParameters(adapterObj, endpoints, pEndpoint, nameParameters, endpointIndex) {
  var deferred = Q.defer();

  var endpoint = pEndpoint;
  var parameters;
  var isJava = adapterObj.isJava;

  inquirer.prompt([
  {
    name: 'parameters',
    message: externalizedStrings.parameterExample,
    validate: function(input) {
      if (input.trim() === '') return true;
      try {
        JSON.parse('[' + input + ']');
      } catch (e) {
        var invalidJSON = errors.getError('INVALIDJSON');
        strings.warn('\n' + invalidJSON.description + '\n');
        return false;
      }
      return true;
    },
    when: function (answer) {
      if (!endpoints[0] && !isJava) return false;
      else if ((answer.endpoint || endpoints[0].value) && !isJava) {
        strings.log('  ' + externalizedStrings.enterParameters);
        return true;
      } else { return false; }
    }
  },
  {
    name: 'callType',
    message: externalizedStrings.callType,
    type: 'list',
    choices: [{ 'name': 'GET', 'value': 'get' },{ 'name': 'POST', 'value': 'post' }],
    when: function (answer) {
      if (isJava || !endpoints[0]) return false;
      else return answer.endpoint || endpoints[0].value;
    }
  }
  ], function (answers) {
    if (!endpoints[0]) {
      strings.info(externalizedStrings.requiresProcedure);
      deferred.reject();
    } else {
      var callType = isJava ? endpoints[endpointIndex].name.split(':')[0].toLowerCase() : answers.callType;
      if (!endpoint) {
        strings.info(externalizedStrings.requiresProcedure);
        deferred.reject();
      }
      else if (!adapterObj.isJava) {
        log.silly('call', 'interactive response (JSON): \'%j\'', answers);

        // The entered arguments string has already been validated as valid JSON - BUT only
        // when JSON parsed with inserted array brackets.  So, if the parsing fails,
        // it's due to multiple comma separated objects being specified without the
        // enclosing array brackets.  So, add the brackets (as was done during validation)
        // to make a valid JSON object.
        try {
          parameters = JSON.parse(answers.parameters.trim());
        } catch (e) {
          log.silly('call', 'forcing contents into an array due to JSON parse warning: '+ e);
          parameters = JSON.parse('[' + answers.parameters.trim() + ']');
        }
        log.silly('call', 'interactive parameters (JSON): \'%j\'', parameters);

        // If the arguments are not enclosed inside an array, make it so.  An
        // array is expected by the downstream consumers.
        parameters = parameters instanceof Array ? parameters : [parameters];
        log.silly('call', 'interactive normalized-as-array parameters (JSON): \'%j\'', parameters);
        serverInfo.scope = getScope(adapterObj, endpoint);
        callAdapter({adapter : adapterObj.name, endpoint: endpoint, parameters: parameters, callType: callType, isJava: adapterObj.isJava})
        .then(function() {
          log.silly('call', 'successfuly called adapter: ' + adapterObj.name);
          deferred.resolve();
        }, function(error) {
          deferred.reject(error);
        });
      } else {
        // Java adapter call
        serverInfo.scope = getScope(adapterObj, endpoint);
        callAdapter({adapter : adapterObj.name, endpoint: endpoint, parameters: nameParameters, callType: callType, isJava: adapterObj.isJava})
        .then(function() {
          log.silly('call', 'successfuly called adapter: ' + adapterObj.name);
          deferred.resolve();
        }, function(error) {
          deferred.reject(error);
        });
      } // end if-else isJava
    } // end if-else !endpoints
  }); // end parameters prompt-function
  return deferred.promise;
} // end promptParameters function

function getScope(adapterObj, endpoint) {
  if (endpoint.indexOf('/') !== 0)
    endpoint = '/' + endpoint;

  var urls = adapterObj.urls;
  for (var i in urls) {
    if (urls[i].uri === endpoint)
      return urls[i].security;
  }
}

function directMode(adapterObj, endpoint, argv, opts) {
  var deferred = Q.defer();

  var badArguments = errors.getError('BADARGUMENTS', 'call');
  var parameters;
  var isJava = adapterObj.isJava;
  var formParams = opts.formParams;
  var headerParams = opts.headerParams;
  var queryParams = opts.queryParams;

  if (adapterObj === undefined)
    deferred.reject(badArguments);

  if (endpoint.length > 1) {
    if (!isJava && (formParams || headerParams || queryParams)) {
      strings.warn(externalizedStrings.paramsWarn);
      formParams = null;
      headerParams = null;
      queryParams = null;
    }

    var parmStr;
    if ((argv.length > 0 && opts.file) || (argv.length > 0 && isJava)) {
      deferred.reject(badArguments);
    } else if (opts.file) {
      try {
        parmStr = fs.readFileSync(opts.file, 'utf8');
        parmStr = parmStr.replace(/^\uFEFF/, '');
        log.silly('call', 'file contents: \'%j\'', parmStr);
      }
      catch(e) {
        var fileNoExists = errors.getError('FILENOEXISTS', path.basename(opts.file));
        deferred.reject(fileNoExists);
      }
    }

    if (!isJava) {
      try {
        if (opts.file && parmStr) {
          try {
            parameters = JSON.parse(parmStr);
          } catch (e) {
            log.silly('call', 'forcing contents into an array due to JSON parse warning: '+ e);
            parameters = JSON.parse('[' + parmStr + ']');
          }
        } else {
          try {
            log.silly('call', 'argv \'%j\'', argv);
            parameters = JSON.parse(argv.join(' '));
          } catch (e) {
            log.silly('call', 'forcing array due to JSON parse warning: '+ e);
            parameters = JSON.parse('[' + argv.join(' ') + ']');
          }
        }
        parameters = parameters instanceof Array ? parameters : [parameters];
        deferred.resolve(parameters);
      } catch (e) {
        log.verbose('call', 'JSON parse failure: '+ e);
        var invalidJSON = errors.getError('INVALIDJSON');
        deferred.reject(invalidJSON);
      }
    } else if (opts.file && parmStr) {
      parameters = JSON.parse(parmStr);
      deferred.resolve(parameters);
    } else {
      validateParams([formParams, queryParams, headerParams]).then(function() {
        parameters = {};
        if (formParams) parameters.FORM = parseParameters(formParams);
        if (headerParams) parameters.HEADER = parseParameters(headerParams);
        if (queryParams) parameters.QUERY = parseParameters(queryParams);
        deferred.resolve(parameters);
      }, function(error){
        deferred.reject(error);
      });
    }
  } else {
    var invalidCallFormat = errors.getError('CALLFORMAT');
    deferred.reject(invalidCallFormat);
  }
  return deferred.promise;
}

function interactiveMode(adapters, profile) {
  var deferred = Q.defer();
  var endpoint = [];

  if (adapters.length <= 0) {
    var noAdaptersErr = errors.getError('NOADAPTERSONSERVER', serverInfo.runtime);
    deferred.reject(noAdaptersErr);
  }

  strings.info(strings.format(externalizedStrings.adapterCallRuntime, serverInfo.runtime));
  inquirer.prompt([
  {
    name: 'adapter',
    message: externalizedStrings.whatAdapterInvoke,
    type: 'list',
    choices: adapters,
    when: function() {
      return adapters.length > 1;
    }
  }], function (answer) {
    var adapter = answer.adapter || adapters[0];
    getAdapterObject(adapter, profile).then(function(adapterObj) {
      if (adapterObj.isJava) {

        // Get the endpoints list for java adapters
        for (var k in adapterObj.urls) {
          if (adapterObj.urls[k].pathParameters ||
              adapterObj.urls[k].formParameters ||
              adapterObj.urls[k].queryParameters ||
              adapterObj.urls[k].headerParameters) {
            var endpointParameters = {};
            var parameters;
            if (adapterObj.urls[k].pathParameters) {
              var pathParameter = adapterObj.urls[k].pathParameters;
              parameters = [];
              for (var l in pathParameter) {
                parameters.push(pathParameter[l].name);
              }
              endpointParameters.PATH = parameters;
            } // end if pathParameters
            if (adapterObj.urls[k].formParameters) {
              var formParameter = adapterObj.urls[k].formParameters;
              parameters = [];
              for(var m in formParameter) {
                parameters.push(formParameter[m].name);
              }
              endpointParameters.FORM = parameters;
            } // end if formParameters
            if (adapterObj.urls[k].queryParameters) {
              var queryParameter = adapterObj.urls[k].queryParameters;
              parameters = [];
              for(var n in queryParameter) {
                parameters.push(queryParameter[n].name);
              }
              endpointParameters.QUERY = parameters;
            } // end if queryParameters
            if (adapterObj.urls[k].headerParameters) {
              var headerParameter = adapterObj.urls[k].headerParameters;
              parameters = [];
              for(var o in headerParameter) {
                parameters.push(headerParameter[o].name);
              }
              endpointParameters.HEADER = parameters;
            } // end if headerParameters
            endpoint.push({'name': adapterObj.urls[k].method + ':' + adapterObj.name+adapterObj.urls[k].uri,'value':k+':'+adapterObj.urls[k].uri,'endpointParameters':endpointParameters});
          } else {
            endpoint.push({'name': adapterObj.urls[k].method + ':' + adapterObj.name+adapterObj.urls[k].uri,'value':k+':'+adapterObj.urls[k].uri,'endpointParameters':null});
          } // end if-else path/form parameters
        }
      } else {
        for (var i in adapterObj.procedures) {
          endpoint.push({'name':adapterObj.name+'/'+adapterObj.procedures[i],'value':adapterObj.procedures[i]});
        }
      }
      if (endpoint.length === 0) {
        var noProceduresError = errors.getError('ADAPTERNOPROCEDURES');
        deferred.reject(noProceduresError);
      }
      promptEndpoints(adapterObj, endpoint)
      .then(function() {
        deferred.resolve();
      })
      .catch(function(error) {
        deferred.reject(error);
      });
    });
  });
  return deferred.promise;
}

function getAdapterObject(adapter, profile) {
  var deferred = Q.defer();

  profile.getAdapter(adapter).then(function (adapterObj) {
    var procedureList = getProcedureList(adapterObj);
    adapterObj.procedures = procedureList;
    adapterObj.name = adapter;
    adapterObj.isJava = (procedureList.length > 0) ? false : true;
    var url;
    var urls = adapterObj.runtimeInfo.resources.paths;
    for (var i in urls) {
      var methods = urls[i];
      for (var j in methods) {
        url = {};
        url.uri = i;
        url.method = j;
        if (methods[j].security)
          url.security = methods[j].security[0]['oauth2'][0];
        var parameters = methods[j].parameters;
        for (var k in parameters) {
          if (parameters[k].in === 'path') {
            if (!url.pathParameters) url.pathParameters = [];
            url.pathParameters.push(parameters[k]);
          }
          if (parameters[k].in === 'formData'){
            if (!url.formParameters) url.formParameters = [];
            url.formParameters.push(parameters[k]);
          }
          if (parameters[k].in === 'header'){
            if (!url.headerParameters) url.headerParameters = [];
            url.headerParameters.push(parameters[k]);
          }
          if (parameters[k].in === 'query'){
            if (!url.queryParameters) url.queryParameters = [];
            url.queryParameters.push(parameters[k]);
          }
        }
        if (!adapterObj.urls) adapterObj.urls = [];
        adapterObj.urls.push(url);
      }
    }
    deferred.resolve(adapterObj);
  }, function (error) {
    deferred.reject(error);
  });
  return deferred.promise;
}

function getAdaptersList(adaptersJson) {
    var adaptersList = [];
    for(var i in adaptersJson.items) {
        adaptersList.push(adaptersJson.items[i].name);
    }
    return adaptersList;
}

function getProcedureList(adapterObj) {
  var procedureList = [];
  var procedures = adapterObj.runtimeInfo.descriptor.procedures;
  for (var i in procedures)
    procedureList.push(i);
  return procedureList;
}

function validateParams(javaParameters) {
  var deferred = Q.defer();

  for (var parameterType in javaParameters) {
    if (javaParameters[parameterType]) {
      var parameters = javaParameters[parameterType];
      var parameterArray = parameters.split(',');
      for (var i in parameterArray) {
        var parameter = parameterArray[i].trim();
        if (!parameter.match(/[\s\S]+[:][\s\S]+/g) ||
            parameter.indexOf(':') === 0 ||
            parameter.indexOf(':') === parameter.length - 1) {
          var error = errors.getError('INVALIDPARAM', parameterArray[i]);
          deferred.reject(error);
        }
      }
    }
  }
  deferred.resolve();
  return deferred.promise;
}

function parseParameters(parameters) {
  var tempObject = {};
  var parameterArray = parameters.split(',');
  for (var index in parameterArray) {
    var parameterSplit = parameterArray[index].split(':');
    tempObject[parameterSplit[0]] = parameterSplit[1];
  }
  return tempObject;
}

function callAdapter (invocation) {
  var deferred = Q.defer();
  var runtime = serverInfo.runtime;
  log.silly('call', 'callAdapter: invocation: %j', invocation);

  var callMethod = invocation.callType.toUpperCase();
  var pathRequest = '/'+(path.join(runtime,'api','adapters',invocation.adapter,invocation.endpoint)).replace(/\\/g,'/');
  if (invocation.isJava && invocation.parameters.hasOwnProperty('PATH'))
    pathRequest = '/'+(path.join(runtime, 'api','adapters',invocation.adapter,encodeURI(invocation.parameters.PATH.pathEndpoint))).replace(/\\/g,'/');
  else if (invocation.parameters.pathEndpoint)
    pathRequest = '/'+(path.join(runtime,'api','adapters',invocation.adapter,encodeURI(invocation.parameters.pathEndpoint))).replace(/\\/g,'/');
  else if (!invocation.isJava && callMethod === 'GET')
    pathRequest += '?params=' + JSON.stringify(invocation.parameters);

  var options = {hostname: serverInfo.host, port: serverInfo.port, runtime: runtime, protocol: serverInfo.protocol + ':'};

  serverSupport.getAuthToken(options, serverInfo.scope, serverInfo.id, serverInfo.secret)
    .then(function(token) {
    var authorization = token.access_token;
    options.path = pathRequest;
    options.method = callMethod;
    options.headers = {
      'Content-Type' : 'application/x-www-form-urlencoded',
      'Authorization':'Bearer '+ authorization,
      'Accept-Charset':'UTF-8'
    };

    var requestCallback = function(response) {
      var content = '';
      var errs;

      if (response.statusCode === 401) {
        var invalidConfidentialClient = errors.getError('INVALIDCONFIDENTIALCLIENT');
        deferred.reject(invalidConfidentialClient);
      }

      response.on('data', function (data) {
        if (data) {
          try {
            content += decodeURI(data);
          } catch(err) {
            log.silly('call', 'Failed to perform decodeURI: ' + err);
            content += data;
          }
          if (data.errors) errs = data.errors;
        }
      }); // end response on data

      response.on('end', function() {
        if (response.statusCode === 200) {
          if (content !== '') {
            try {
              var results = JSON.parse(content.replace(/^\/\*-secure-([\s\S]*)\*\//, '$1'));
              strings.log(JSON.stringify(results, null, 2));

              if (results.errors && results.errors.length) {
                strings.fatal(externalizedStrings.logErrorLabel);
                results.errors.forEach(function (error) {
                  strings.fatal(error);
                });
              }
              if (results.warnings && results.warnings.length) {
                strings.warn(externalizedStrings.logWarningLabel);
                results.warnings.forEach(function (warning) {
                  strings.warn(warning);
                });
              }
            } catch (e) {
              strings.log(content);
            }
          } else {
            strings.warn(externalizedStrings.emptyResponse);
          }
        } else {
          console.log((strings.format(externalizedStrings.statusCodeMessage,response.statusCode,http.STATUS_CODES[response.statusCode]))[response.statusCode > 400 ? strings.theme.error : strings.theme.warn]);
          if (content !== '') strings.log(JSON.parse(content));
        }
        deferred.resolve();
      }); // end response on end
    };

    var request, formattedParm;
    options.path = encodeURI(options.path);
    console.log(strings.format(externalizedStrings.callingMessage, invocation.callType.toUpperCase()), ('\''+decodeURI(options.path)+'\'')[strings.theme.info]);
    strings.log(externalizedStrings.responseMessage);

    if (invocation.isJava) {
      if (invocation.parameters) {
        if (invocation.parameters.hasOwnProperty('FORM')) formattedParm = qs.stringify(invocation.parameters.FORM);
        if (invocation.parameters.hasOwnProperty('QUERY')) options.path += '?'+ qs.stringify(invocation.parameters.QUERY);
        if (invocation.parameters.hasOwnProperty('HEADER')) {
          for (var header in invocation.parameters.HEADER) {
            options.headers[header] = invocation.parameters.HEADER[header];
          }
        } else if (invocation.headerParams) {
          for (var headerK in invocation.headerParams) {
            var tempHeader = invocation.headerParams[headerK].split(':');
            options.headers[tempHeader[0]] = tempHeader[1];
          }
        }
        if (!formattedParm) {
          formattedParm = JSON.stringify(invocation.parameters);
          options.headers["Content-Type"] = "application/json";
        }
      }

      log.silly('call', 'callAdapter: issuing java adapter request: \'%j\'', options);
      request = getRequest(options, requestCallback);

      if (callMethod === 'POST' || callMethod === 'PUT') {
        log.silly('call', 'callAdapter: writing form data:\n', formattedParm);
        request.write(formattedParm);
      }
    } else {
      log.silly('call', 'callAdapter: issuing non-java adapter request: \'%j\'', options);
      request = getRequest(options, requestCallback);

      if (callMethod === 'POST' || callMethod === 'PUT') {
        formattedParm = qs.stringify(invocation.parameters);
        request.write(formattedParm);
      }
    } //end-else isJava

    request.on('error', function (e) {
      // If there's an error with the request, log it and resolve the deferral
      if (e.code === 'ECONNREFUSED') {
        strings.fatal(e.message);
        deferred.reject(e.code);
      }
      else {
        strings.log(externalizedStrings.errorInvoking);
        strings.log(e.message, strings.theme.error);
      }
      deferred.reject();
    }); // end resquest on error
    request.end();
  }).fail(function(error) {
    deferred.reject(error);
  });
  return deferred.promise;
}

function getRequest(options, callback) {
  serverSupport.getCertificate(options, moduleName);

  if (options.protocol === 'https:') {
    log.silly(moduleName, 'performing https request');
    return https.request(options, callback);
  } else {
    log.silly(moduleName, 'performing http request');
    return http.request(options, callback);
  }
}

// return the path to the md file (relative to basePath) for the specific command
module.exports.getHelpFilePath = function(basePath) {
  return path.join(basePath, 'mdo-adapter-actions', 'doc', 'call.txt');
};
