/*
  Licensed Material - Property of IBM

  (C) Copyright 2015, 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 strings = require('ibm-strings');
var path = require('path');
var Q = require('q');
var fs = require('fs');
var externalizedStrings = require('../externalizedStrings');
var inquirer = require('inquirer');
var consts = require('./preview-consts');
var errors = require('mdo-errors');
var manager = require('platform-manager');
var cordovaSupport = require('mdo-cordova-support');
var global_config = require("mdo-cmd-config").config_api; //require the api
var nopt = require('nopt');
var log = require('npmlog');
var open = require("open");
var findExtensions = require('pluggington');
var serverDetailsDef = Q.defer();
var proxyDef = Q.defer();
var deferred;
var watchDef;
var bsDef;
//get main server port and host
var getAuthToken = require('mdo-server-support').getAuthToken;
var serverSupport = require('mdo-server-support');
var getAdapter = require('mdo-server-support/lib/rest-client-api/rest-client-api.js').getAdapter;
var serverInfo = {}; // server info
serverInfo.protocol = 'http';
serverInfo.host = 'localhost';
serverInfo.port = '9080';
serverInfo.path = '';
serverInfo.contextRoot = 'mfpadmin';
serverInfo.runtime = 'mfp';
serverInfo.serverUsername = 'admin';
serverInfo.serverPassword ='admin';
var serverPathForProxy = ''; //protocol host port
var nodeModulePath;

function logSilly(message) {
  log.silly(consts.MODULE, message);
}
function logVerbose(message) {
  log.verbose(consts.MODULE, message);
}
/**
 * Throws an error message and finish the execution with the code for the given
 * error.
 */
function throwError(error) {
  strings.fatal(error.description);
  deferred.reject(error);
}
function previewMain(args) {
  deferred = Q.defer();
  var installedPlatforms;
  logVerbose('Starting preview.');
  if (global.MDO_CLI_LOG_LEVEL)
    log.level = global.MDO_CLI_LOG_LEVEL;
  manager.detectPlatforms().then(function(info) {
    process.chdir(info.rootDir);
  if(!fs.existsSync(path.join(info.rootDir,'plugins','cordova-plugin-mfp'))){
    var noPluginError = errors.getError('CORDOVAMFPPLUGINNOTINSTALLED');
    throwError(noPluginError);
    process.exit(noPluginError.errno);
    }
    installedPlatforms = info.platforms.map(function (element) {
      return element.platform;
    });
    args = nopt(consts.LONG_HAND_ARGS, consts.SHORT_HAND_ARGS, args, 0);
    args = parseArguments(args, installedPlatforms);
  if(args.pidMode === 'on'){
       console.log('PID: ' + process.pid);
  }
  //Direct Mode
    if (args && args.platforms){
       getServerInfoDirect(args,info);
  }
    else if (args && !args.platforms && !args.mode && !args.type){//Interactive Mode
       getServerInfoInteractive(args,info,installedPlatforms);
  }
    else{
       throwError(errors.getError('NO_PLATFORM_SPECIFIED'));
  }
  }, function(err) {
    logVerbose('Caught error from platforms-manager');
    throwError(err);
  });
  return deferred.promise;
}
/*
 * Parses CLI arguments and returns an object of the form:
 * {
 *  mode: <consts.WC_MODE, consts.WP_MODE, consts.S_MODE, or undefined if not
 *         specified>,
 *  platforms: <Array of platform strings or undefined if platform not
 *              specified>,
 *  type: <consts.MBS, consts.BROWSER, or undefined if type not specified>
 * }
 * to be used by directModePreview and interactiveModePreview
 */
function parseArguments(args, installedPlatforms) {
  var opts;
  logVerbose('Parsing arguments.');
  opts = {};
  opts = parsePlatforms(opts, args, installedPlatforms);
  opts = parseMode(opts, args);
  opts = parseType(opts, args);
  opts = parseServerMode(opts,args);
  opts = parsePIDMode(opts,args);
  opts = parseClientID(opts, args);
  opts = parseSecret(opts, args);
  logSilly('Preview options: ' + opts);
  return opts;
}
function parsePlatforms(opts, args, installedPlatforms) {
  logVerbose('Parsing platforms');
  if (args.argv.remain.length > 0)
    opts.platforms = args.argv.remain[0].split(',');
  // Determine Direct, or Interactive mode
  if (opts.platforms && opts.platforms.length > 0) {
    // Filter out invalid platforms
    opts.platforms = opts.platforms.filter(function (platform) {
      return installedPlatforms.indexOf(platform) > -1;
    });
    // No valid platforms, just use installedPlatforms
    if (opts.platforms.length === 0) {
      opts.platforms = undefined;
      throwError(errors.getError('NO_PLATFORM_SPECIFIED')); // no valid platforms
      opts = undefined;
    }
  } else
    opts.platforms = undefined;
  return opts;
}
function parseMode(opts, args) {
  logVerbose('Parsing mode.');
  if (args['watch+copy'] && !args['watch+prepare'] && !args.serve) {
    opts.mode = consts.WC_MODE;
  } else if (!args['watch+copy'] && args['watch+prepare'] && !args.serve) {
    opts.mode = consts.WP_MODE;
  } else if (!args['watch+copy'] && !args['watch+prepare'] && args.serve) {
    opts.mode = consts.S_MODE;
  } else if (args['watch+copy'] || args['watch+prepare'] || args.serve){
    throwError(errors.getError('MORE_THAN_ONE_MODE_SPECIFIED'));
    opts = undefined;
  } else if (args.mode === undefined)
    opts.mode = undefined;
  return opts;
}
function parseServerMode(opts, args) {
  logVerbose('Parsing server mode.');
  if (args['noserver']) {
    opts.serverMode = 'off';
  } else{
  opts.serverMode = 'on';
  }
  return opts;
}
function parsePIDMode(opts, args) {
  logVerbose('Parsing PID mode.');
  if (args['pid']) {
    opts.pidMode = 'on';
  } else{
  opts.pidMode = 'off';
  }
  return opts;
}
function parseType(opts, args) {
  logVerbose('Parsing type.');
  if (opts.platforms && opts.platforms.length > 0 && opts.platforms[0] === consts.BROWSER) {
      if (args.type === consts.MBS) {
          // Browser does not support MBS
          throwError(errors.getError('BROWSERNOTSUPPORTMBS', opts.platforms[0]));
          opts = undefined;
          return opts;
      }
  }
  if (args.type === consts.BROWSER || args.type === consts.MBS ||
      args.type === undefined)
    opts.type = args.type;
  else {
    throwError(errors.getError('INVALID_PREVIEW_TYPE'));
    opts = undefined;
  }
  return opts;
}
function parseClientID(opts, args) {
  logVerbose('Parsing client ID.');
  if (args.clientid === undefined){
    opts.clientid = 'test';
  }
  else {
  opts.clientid =  args.clientid;
  }
  return opts;
}
function parseSecret(opts, args) {
  logVerbose('Parsing secret.');
  if (args.secret === undefined){
    opts.secret = 'test';
  }
  else {
  opts.secret =  args.secret;
  }
  return opts;
}
function interactiveModePreview(opts, appDir, installedPlatforms,serverDetails) {
  var platformChoices = installedPlatforms.filter(function(platform) {
    return (platform !== 'windows8' && platform !== 'windowsphone8');
  });
  var inquirerPromptType = [{
    name: 'type',
    type: 'list',
    choices: [{
        name: externalizedStrings.browserRenderingType,
        value: consts.BROWSER
      }, {
        name: externalizedStrings.mobileBrowserSimulatorOption,
        value: consts.MBS
      }
    ],
    message: externalizedStrings.previewTypes,
    default: consts.BROWSER
  }];
  var inquirerPromptPlatform = [{
    name: 'platforms',
    type: 'checkbox',
    choices: platformChoices,
    message: externalizedStrings.selectPlatformsForPreview,
    validate: function(input) {
      return input.length === 0 ?
             externalizedStrings.selectPlatformMessage : true;
    },
    when: function() {
      return installedPlatforms.length !== 1;
    }
  }];
  inquirer.prompt(inquirerPromptType, function(answers1) {
      // Remove browser platform option if MBS was chosen
      if (answers1.type === consts.MBS) {
          var browserIndex = platformChoices.indexOf(consts.BROWSER);
          if (browserIndex > -1) {
              platformChoices.splice(browserIndex, 1);
              inquirerPromptPlatform.choices = platformChoices;
          }
          if (platformChoices && platformChoices.length === 0) {
              throwError(errors.getError('BROWSERNOTSUPPORTMBS', consts.BROWSER));
          }
      }
      inquirer.prompt(inquirerPromptPlatform, function(answers2) {
          answers2.mode = consts.WC_MODE; // default mode for interactive mode
          answers2.platforms = answers2.platforms || installedPlatforms;
          answers2.clientid = opts.clientid;
          answers2.secret = opts.secret;
          answers2.type = answers1.type;
          beginWatch(answers2, appDir,serverDetails);
      });
  });
}
function directModePreview(opts, appDir,serverDetails) {
  var keyVals;
  var defaultPreviewType;
  logVerbose('Starting Direct mode.');
  keyVals = global_config.values;
  defaultPreviewType = consts.BROWSER;
  global_config.getDefaultPreviewType().then(
    function(val) {
      // Set the default preview type
      if (val === keyVals.SIMPLE_BROWSER)
        defaultPreviewType = consts.BROWSER;
      else if (val === keyVals.MBS)
        defaultPreviewType = consts.MBS;
      opts.type = opts.type || defaultPreviewType;
      opts.mode = opts.mode || consts.WC_MODE;
      beginWatch(opts, appDir,serverDetails);
    },
    function(err){
      //err handling
      throwError(err);
    }
  );
}
function setupWatchEvents(previewWatch, bs,appDir,opts) {
  logVerbose('Setting up file watch events.');

  // The watch is read, start Browser Sync
  previewWatch.once(consts.PREVIEW_START,
    function() {
      //We can be sure prepare is finished and we can check for <appPath>\platforms\<platform>\[assets]\www\plugins\cordova-plugin-mfp\worklight\       //this ensures the correct cordova version is used, else error
      for(var i=0; i< opts.platforms.length; i++){
           if(opts.platforms[i] === 'android'){
               nodeModulePath = path.join(appDir, path.join('node_modules', 'cordova-android', 'VERSION'));//path to cordova-android version file
               if(fs.existsSync(nodeModulePath)){//cordova plugman >=7.x.x
                   var cordovaAndroidVersion = fs.readFileSync(nodeModulePath);
                   cordovaAndroidVersion = parseInt(cordovaAndroidVersion); //cordova android version
                   if(cordovaAndroidVersion >= 7){//cordova android version >= 7.x.x
                       if(!fs.existsSync(path.join(appDir, 'platforms', opts.platforms[i], 'app', 'src', 'main', 'assets', 'www', 'plugins', 'cordova-plugin-mfp', 'worklight'))){
                           throwError(errors.getError('INVALIDPLATFORMVERSION', opts.platforms[i]));
                       }
                   } else {//cordova android version <7.x.x
                       if(!fs.existsSync(path.join(appDir, 'platforms', opts.platforms[i], 'assets', 'www', 'plugins', 'cordova-plugin-mfp', 'worklight'))){
                           throwError(errors.getError('INVALIDPLATFORMVERSION', opts.platforms[i]));
                       }
                   }
               } else { //cordova plugman <7.x.x and cordova android <7.x.x
                   if(!fs.existsSync(path.join(appDir, 'platforms', opts.platforms[i], 'assets', 'www', 'plugins', 'cordova-plugin-mfp', 'worklight'))){
                       throwError(errors.getError('INVALIDPLATFORMVERSION', opts.platforms[i]));
                   }
               }
           } else {
                   if(!fs.existsSync(path.join(appDir, 'platforms', opts.platforms[i], 'www', 'plugins', 'cordova-plugin-mfp', 'worklight'))){
                    throwError(errors.getError('INVALIDPLATFORMVERSION', opts.platforms[i]));
                   }
            }
      }
      logVerbose('Watch is ready');
      watchDef.resolve();
    }
  );
  // Browser Sync must be reloaded as a change occurred
  previewWatch.on(consts.PREVIEW_RELOAD,
    function() {
      logVerbose('reload');
      bs.reload();
    }
  );
  // Preview watch has failed
  previewWatch.once(consts.PREVIEW_ERROR,
    function(message, stackTrace) {
      logSilly('message: ' + message);
      logSilly('stack: ' + stackTrace);
      this.stopWatch();
      bs.exit();
      throwError(errors.getError('CORDOVANOTINSTALLED'));//cordova is not installed
    }
  );
}
function setupBrowserSyncEvents(bs) {
  logVerbose('Setting up Browser Sync events.');
  // Browser Sync is ready
  bs.emitter.once('init',
    function () {
      bsDef.resolve();
    }
  );
}
function setupBrowserLaunchEvent(opts, appDir) {
  logVerbose('Setting up browser launch event.');
  // Launch the browser when the promises are resolved
  Q.all([watchDef.promise, bsDef.promise,serverDetailsDef.promise,proxyDef.promise]).then(
    function () {
      launchBrowser(opts, appDir);
    },
    function (err) {
      logVerbose(err);
      throwError(errors.getError('BROWSERSYNCFAILED'));  // browser sync launch fails
    }
  );
}
function launchBrowser(opts, appDir) {
  console.log(externalizedStrings.launchingBrowser);
  cordovaSupport.parse(appDir, 'config.xml').then(
    function(data) {
      var mainFile = data['app.mainfile'];

      global_config.getDevelopmentBrowser().then(
        function(browser) {
          if (opts.type === consts.BROWSER) {
            opts.platforms.forEach(
              function(platform) {
                if(browser === global_config.values.DEFAULT_BROWSER)
                  open(getBrowserURL(platform, mainFile));
                else
                  open(getBrowserURL(platform, mainFile), browser); // browser
              }
            );
          } else if (opts.type === consts.MBS) {
            if (browser === global_config.values.DEFAULT_BROWSER)
              open(getMBSURL(opts.platforms, mainFile));
            else
              open(getMBSURL(opts.platforms, mainFile), browser);
          }
        },
        function(err){
          //err handling
          throwError(err);
        }
      );
    }
  );
}
function beginWatch(opts, appDir,serverDetails) {
  var PreviewWatch = require('./preview-watch');
  var bs = require('browser-sync');
  var previewWatch;
  logVerbose('Starting watch.');
  watchDef = Q.defer();
  bsDef = Q.defer();
  setupBrowserSyncEvents(bs);
  setupBrowserLaunchEvent(opts, appDir);
  previewWatch = new PreviewWatch(opts.mode,appDir, opts.platforms);
  setupWatchEvents(previewWatch, bs,appDir,opts);
  previewWatch.startWatch();
  beginServer(opts, appDir, bs,serverDetails);
}
function beginServer(opts, rootDir, bs,serverDetails) {
  strings.warn(externalizedStrings.ignoreContentSecurityPolicy);
  startProxyServer(rootDir,opts,serverDetails);
  var MBS_PATH = getMBSExtension();
  if (!MBS_PATH) {
    strings.warn(externalizedStrings.mbsExtensionNotFound);
    opts.type = consts.BROWSER;
  }
  var browserSyncOptions = {
    // blacklist excludes the MBS page from browsersync so that it doesn't refresh
    snippetOptions: { blacklist: ["/index.html*/**"] },
    server: opts.type === consts.MBS ? { baseDir: [MBS_PATH, './platforms'], middleware: function (req, res, next) {
            res.setHeader('Access-Control-Allow-Origin', '*');
            res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
            res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
            next();
        } } :
            { baseDir: './platforms', middleware: function (req, res, next) {
            res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
            res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
            next();
        } },
    open: false,
    port: consts.DEFAULT_PORT,
    rewriteRules: consts.REWRITE_RULES,
  notify : false,
    files: false,
    online: true
  };

  bs.init(browserSyncOptions);
}
function getMBSExtension() {
  var mbsExtension = findExtensions(global.MDO_MAIN_EXTENSIONS_PATH, 'mdo.mbs');
  if (mbsExtension)
    return mbsExtension[0].export.getPath();
}
function getServerInfoInteractive(args,info,installedPlatforms) {
  if(args.serverMode === 'on'){ //server mode to connect adapters is on
      serverSupport.getServerDetails().then(function (serverDetailsReturned) {
         var serverDetails = serverDetailsReturned;
         serverDetails.fullURL = serverDetails.protocol + '://' + serverDetails.host + ':' + serverDetails.port;
         if(typeof serverDetails.path !== 'undefined' && serverDetails.path.length > 1){
            serverDetails.fullURL = serverDetails.fullURL + serverDetails.path;
            serverInfo.path = serverDetails.path;
         }
         else{
            serverInfo.path = "";
         }
         serverInfo.host = serverDetails.host;
         serverInfo.port = serverDetails.port;
         serverInfo.protocol = serverDetails.protocol;
         serverInfo.runtime = serverDetails.runtime;
         serverInfo.contextRoot = serverDetails.contextRoot;
         serverPathForProxy = serverDetails.fullURL;
         serverInfo.serverUsername = serverDetails.userName;
         serverInfo.serverPassword = serverDetails.userPassword;
         var serverDetailsForAdapterCall = { adapter: 'adapterName', runtime: serverInfo.runtime,host: serverInfo.host,port: serverInfo.port,path: serverInfo.path,userName: serverInfo.serverUsername, userPassword: serverInfo.serverPassword,contextRoot: serverInfo.contextRoot};
         interactiveModePreview(args, info.rootDir, installedPlatforms,serverDetailsForAdapterCall);
         serverDetailsDef.resolve();
      },function(err) {
         strings.warn(err); //if server details fail warn via CLI, then start proxy
         var serverDetailsForAdapterCall = { adapter: 'adapterName', runtime: serverInfo.runtime,host: serverInfo.host,port: serverInfo.port,path: serverInfo.path,userName: serverInfo.serverUsername, userPassword: serverInfo.serverPassword,contextRoot: serverInfo.contextRoot};
         interactiveModePreview(args, info.rootDir, installedPlatforms,serverDetailsForAdapterCall);
         serverDetailsDef.resolve();
      });
   }else{//server mode is off
         var serverDetailsDefault = {};
         serverDetailsDefault.fullURL = 'http' + '://' + 'localhost' + ':' + '9080';
         serverPathForProxy = serverDetailsDefault.fullURL;
         var serverDetailsForAdapter = { adapter: 'adapterName', runtime: serverInfo.runtime,host: serverInfo.host,port: serverInfo.port,path: serverInfo.path,userName: serverInfo.serverUsername, userPassword: serverInfo.serverPassword,contextRoot: serverInfo.contextRoot};
         interactiveModePreview(args, info.rootDir, installedPlatforms,serverDetailsForAdapter);
         serverDetailsDef.resolve();
   }

}
function getServerInfoDirect(args,info) {
  if(args.serverMode === 'on'){ //server mode to connect adapters is on
     serverSupport.getServerDetails().then(function (serverDetailsReturned) {//get server support info and pass it into direct and interactive preview modes
         var serverDetails = serverDetailsReturned;
         serverDetails.fullURL = serverDetails.protocol + '://' + serverDetails.host + ':' + serverDetails.port;
         if(typeof serverDetails.path !== 'undefined' && serverDetails.path.length > 1){
          serverDetails.fullURL = serverDetails.fullURL + serverDetails.path;
          serverInfo.path = serverDetails.path;
         }
         else{
          serverInfo.path = "";
         }
         serverInfo.host = serverDetails.host;
         serverInfo.port = serverDetails.port;
         serverInfo.protocol = serverDetails.protocol;
         serverInfo.runtime = serverDetails.runtime;
         serverInfo.contextRoot = serverDetails.contextRoot;
         serverPathForProxy = serverDetails.fullURL; //protocol host port (instanceID -> if LitePlan)
         serverInfo.serverUsername = serverDetails.userName;
         serverInfo.serverPassword = serverDetails.userPassword;
         var serverDetailsForAdapterCall = { adapter: 'adapterName', runtime: serverInfo.runtime,host: serverInfo.host,port: serverInfo.port,path: serverInfo.path,userName: serverInfo.serverUsername, userPassword: serverInfo.serverPassword,contextRoot: serverInfo.contextRoot};
         directModePreview(args, info.rootDir,serverDetailsForAdapterCall);
         serverDetailsDef.resolve();
      },function(err) {
         strings.warn(err); //if server details fail warn via CLI, then start proxy
         var serverDetailsForAdapterCall = { adapter: 'adapterName', runtime: serverInfo.runtime,host: serverInfo.host,port: serverInfo.port,path: serverInfo.path,userName: serverInfo.serverUsername, userPassword: serverInfo.serverPassword,contextRoot: serverInfo.contextRoot};
         directModePreview(args, info.rootDir,serverDetailsForAdapterCall);
         serverDetailsDef.resolve();
      });
   }else{//server mode is off
         var serverDetails = {};
         serverDetails.fullURL = 'http' + '://' + 'localhost' + ':' + '9080'; //set defaults
         serverPathForProxy = serverDetails.fullURL; //protocol host port (instanceID -> if LitePlan)
         var serverDetailsForAdapterCall = { adapter: 'adapterName', runtime: serverInfo.runtime,host: serverInfo.host,port: serverInfo.port,path: serverInfo.path,userName: serverInfo.serverUsername, userPassword: serverInfo.serverPassword,contextRoot: serverInfo.contextRoot};
         directModePreview(args, info.rootDir,serverDetailsForAdapterCall);
         serverDetailsDef.resolve();
   }
}
function getBrowserURL(platform, mainFile) {
  var hostMain = 'localhost';
  var portMain = consts.DEFAULT_PORT;
  var url = 'http://' + hostMain + ':' + portMain + '/' + platform;
 if (platform === consts.IOS)
    url += '/www/' + mainFile;
  else if (platform === consts.ANDROID)
    {
      if(fs.existsSync(nodeModulePath)){ //cordova plugman >=7.x.x
      var  cordovaAndroidVersion=fs.readFileSync(nodeModulePath);
         cordovaAndroidVersion=parseInt(cordovaAndroidVersion);//cordova android version
       if(cordovaAndroidVersion>=7){//cordova android >=7.x.x
              url += '/app/src/main/assets/www/' + mainFile;
          }else{//cordova android <7.x.x
             url += '/assets/www/' + mainFile;
          }
       }else{//cordova plugman <7.x.x and cordova android <7.x.x
        url += '/assets/www/' + mainFile;
     }

  }
  else if (platform === consts.WIN)
    url += '/www/' + mainFile;
  else if (platform === consts.BROWSER)
    url += '/www/' + mainFile;
  logSilly(url);
  return url;
}
function getMBSURL(platforms, mainFile) {
  var hostMain = 'localhost';
  var portMain = consts.DEFAULT_PORT;
  var mbsURL = 'http://' + hostMain + ':' + portMain + '/index.html';
  var mbsPlatformsArg = [];
  // mbs doesn't like 'ios', likes 'ios.iphone'
  platforms.forEach(function(platform) {
    if (platform.indexOf(consts.IOS) !== -1) mbsPlatformsArg.push(consts.IOS_IPHONE);
    else if (platform.indexOf(consts.WIN) !== -1) mbsPlatformsArg.push(consts.WIN_PHONE);
    else mbsPlatformsArg.push(platform);
  });
  // Create query parameters, convert to URL
  var queryParameters = {
    webpage: getBrowserURL(platforms[0], mainFile),
    smartPreview: platforms.length > 1 ? "On" : "Off",
    platform: mbsPlatformsArg.join()
  };
  var queryString = Object.keys(queryParameters).reduce(function(arr, key) {
    arr.push(key + '=' + queryParameters[key]);
    return arr;
  }, []).join('&');
  return mbsURL + '?' + queryString;
}

function startProxyServer(rootDir,opts,serverDetails) {
  var http = require('http'), httpProxy = require('http-proxy');
  // Create your proxy server and set the target in the options.
  var proxy = httpProxy.createProxyServer({});

  proxy.on('proxyReq', function (proxyReq, req, res) {
    res.setHeader('Access-Control-Allow-Origin', req.headers['origin'] || 'localhost:6015');
    res.setHeader('Access-Control-Allow-Methods', 'POST, GET','OPTIONS','HEAD','PUT');
  });

  // Listen for the `proxyRes` event on `proxy`.
  proxy.on('proxyRes', function (proxyRes, req, res) {
    var tempHeaders = proxyRes.headers;
    tempHeaders['Access-Control-Allow-Origin'] = req.headers['origin'] || 'localhost:6015';
    tempHeaders['Access-Control-Allow-Methods'] = 'POST,PUT,DELETE,GET,OPTIONS,HEAD,PATCH';
    tempHeaders['Access-Control-Request-Headers'] = Object.keys(proxyRes.headers).join(', ');
    tempHeaders['Access-Control-Allow-Headers'] = Object.keys(req.headers).join(', ') + ', ' + 'X-JSON, WWW-Authenticate';
    tempHeaders['Access-Control-Expose-Headers'] = Object.keys(proxyRes.headers).join(', ') + ', ' + 'X-JSON, WWW-Authenticate';

    res.headers = tempHeaders;
    proxyRes.headers = tempHeaders;
  });
  // Create your custom server and just call `proxy.web()` to proxy
  // a web request to the target passed in the options
  // also you can use `proxy.ws()` to proxy a websockets request
  var server = http.createServer(function(req, res) {
    // Set CORS headers
    if ( req.method === 'OPTIONS' ) {
      var tempHeaders = req.headers;
      tempHeaders['Access-Control-Allow-Origin'] = req.headers['origin'] || 'localhost:6015';
      tempHeaders['Access-Control-Allow-Methods'] = 'POST,PUT,DELETE,GET,OPTIONS,HEAD,PATCH';
      if(req.headers['access-control-request-headers'] !== undefined) {
        tempHeaders['Access-Control-Allow-Headers'] = Object.keys(req.headers).join(', ') + ', ' + req.headers['access-control-request-headers'] + ', ' + 'X-JSON, WWW-Authenticate';
        tempHeaders['Access-Control-Request-Headers'] = Object.keys(req.headers).join(', ') + ', ' + req.headers['access-control-request-headers'] + ', ' + 'X-JSON, WWW-Authenticate';
      } else {
        tempHeaders['Access-Control-Allow-Headers'] = Object.keys(req.headers).join(', ') + ', ' + 'X-JSON';
        tempHeaders['Access-Control-Request-Headers'] = Object.keys(req.headers).join(', ') + ', ' + 'X-JSON';
      }
      req.headers = tempHeaders;

      res.setHeader('Access-Control-Allow-Origin', req.headers['origin'] || 'localhost:6015');
      if(req.headers['access-control-request-headers'] !== undefined) {
        res.setHeader('Access-Control-Allow-Headers', Object.keys(req.headers).join(', ') + ', ' + req.headers['access-control-request-headers'] + ', ' + 'X-JSON, WWW-Authenticate');
        res.setHeader('Access-Control-Request-Headers', Object.keys(req.headers).join(', ') + ', ' + req.headers['access-control-request-headers'] + ', ' + 'X-JSON, WWW-Authenticate');
      } else {
        res.setHeader('Access-Control-Allow-Headers', Object.keys(req.headers).join(', ') + ', X-JSON, WWW-Authenticate');
        res.setHeader('Access-Control-Request-Headers', Object.keys(req.headers).join(', ')  + ', X-JSON, WWW-Authenticate');
      }
      res.setHeader('Access-Control-Allow-Methods', 'POST,PUT,DELETE,GET,OPTIONS,HEAD,PATCH');
      res.writeHead(200, tempHeaders);
      res.end();
      return;
    }else{
      res.setHeader('Access-Control-Allow-Origin', req.headers['origin'] || 'localhost:6015');
      res.setHeader('Access-Control-Allow-Methods', 'POST,PUT,DELETE,GET,OPTIONS,HEAD,PATCH');

      if(req.headers['access-control-request-headers'] !== undefined) {
        res.setHeader('Access-Control-Expose-Headers', Object.keys(req.headers).join(', ') + ', ' + req.headers['access-control-request-headers'] + ', ' + 'X-JSON, WWW-Authenticate');
        res.setHeader('Access-Control-Allow-Headers', Object.keys(req.headers).join(', ') + ', ' + req.headers['access-control-request-headers'] + ', ' + 'X-JSON, WWW-Authenticate');
      } else {
        res.setHeader('Access-Control-Expose-Headers', Object.keys(req.headers).join(', ') + ', ' + 'X-JSON, WWW-Authenticate');
        res.setHeader('Access-Control-Allow-Headers', Object.keys(req.headers).join(', ') + ', ' + 'X-JSON, WWW-Authenticate');
      }
    }

    if(!req.url.startsWith('/')) {
      req.url = '/' + req.url;
    }
    if(!req.url.startsWith('/' + serverInfo.runtime)) {
      req.url = '/' + serverInfo.runtime + req.url;
    }

    // Request and add authorization token to request, if needed
    var splitUrl = req.url.split('?')[0].split('/');
    var adapterName = splitUrl[4];
    var adapterMethod = splitUrl[5];
    var requestMethodType = req.method.toLowerCase();
    var requestpath = req.url.slice(req.url.indexOf(adapterMethod) - 1);
    serverDetails.adapter = adapterName;
    getAdapter(serverDetails).then(function(adapterObj) { // get adapter details with the scope
      var adapterScope;
      var adapterMethodJson = adapterObj['runtimeInfo']['resources']['paths'];

      // Find adapter path that is close a match to a request path
      // Sort the available paths for an adapter in descending order based on length
      // Convert each path into an Regular expression and traverse through each path
      // If request path matches with an avaible path, extract OAuth scope from the JSON and break the loop.
      var keyArray = Object.keys(adapterMethodJson);
      keyArray.sort(function(a, b){
        return b.length - a.length;
      });

      keyArray.forEach(function(item){
        var itemRegEx = item.replace(/{.*?}/g,".*");
        itemRegEx = itemRegEx.replace(/\//g,"\\\/");
        var regEx = new RegExp(itemRegEx + "#");
        var matchingurl = regEx.test(requestpath + "#")
        if(matchingurl == true && adapterScope == undefined && adapterMethodJson[item][requestMethodType] !== undefined){
          if(adapterMethodJson[item][requestMethodType]['security'] !== undefined) {
            adapterScope = adapterMethodJson[item][requestMethodType]['security'][0]['oauth2'][0];
          }
      }});

      // Proxy the request, handle authentication process for protected resources
      if(adapterScope !== undefined) {
        getAuthToken({"hostname":serverInfo.host,"protocol":serverInfo.protocol + ':',"port":serverInfo.port,
              "runtime":serverInfo.runtime},adapterScope,opts.clientid,opts.secret).then(function(authorization) {
          var tempHeaders = req.headers;
          tempHeaders['authorization'] = 'Bearer ' + authorization.access_token;
          req.headers = tempHeaders;
          proxy.web(req, res, { target: serverPathForProxy, changeOrigin: true});
        });
      } else {
        proxy.web(req, res, { target: serverPathForProxy, changeOrigin: true});
      }
    });
  });
  server.listen(6015);
  server.on('error', function (e) {
    if(e.code === 'EADDRINUSE'){
      throwError(errors.getError('PREVIEWPORTINUSE'));
    }else{
      throwError(e);
    }
  });
  proxyDef.resolve();
}
module.exports.help = function() {
  return path.resolve(__dirname, '..', 'doc', 'preview.txt');
};
module.exports.run = previewMain;
