/*
	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 Q = require('q');
var inquirer = require('inquirer');
var path = require('path');
var nopt = require('nopt');

var strings = require('ibm-strings');
var errors = require('mdo-errors');
var serverSupport = require('mdo-server-support');
var profileIO = serverSupport.profileIO;
var externalizedStrings = require('./externalizedStrings');
var serverRegEx = require('./server-add').serverRegEx;
var urlFormatter = require('url');


module.exports.validate = function(args) {
  var deferred = Q.defer();

  var opts = nopt({
    password: String
  }, {
    p: '--password'
  }, args, 0);

  if (opts.argv.remain.length > 0) {
    if (opts.argv.remain.length === 1) {
      var serverName = opts.argv.remain[0];
      if (!serverName.match(serverRegEx)) {
        var invalidServerName = errors.getError('INVALIDSERVERNAME');
        deferred.reject(invalidServerName);
      }
      else {
        deferred.resolve(opts);
      }
    }
    else { // invalid arguments found
      var badArguments = errors.getError('BADSERVERARGS', 'info');
      deferred.reject(badArguments);
    }
  }
  else {
    deferred.resolve(opts);
  }
  return deferred.promise;
};

module.exports.run = function(args, profiles) {
  var deferred = Q.defer();

  if (args.argv.remain.length === 0) {
    if (profiles.defaultServer === undefined) {
      var noprofiles = errors.getError('MISSINGSERVERDEFINITION');
      deferred.reject(noprofiles);
    }
    else {
      printAllServersInfo(profiles);
      // Resolve deferral
      deferred.resolve();
    }
  }
  else {
    getServerDetails(args, profiles)
      .then(function() {
        deferred.resolve();
      })
      .fail(function(err) {
        deferred.reject(err);
      });
  }
  return deferred.promise;
};

// return the help object for the main mdo help command to show
module.exports.getMainHelp = function() {
  var help = {
    "SERVERS" : [
      {"server info" : externalizedStrings.serverInfoDescription}
    ]
  };
  return help;
};

// return the path to the help file (relative to basePath) for the specific command
module.exports.getHelpFilePath = function(basePath) {
  return path.join(basePath, 'mdo-cmd-server', 'doc', 'info.txt');
};

module.exports.printInfo = function() {
  profileIO.getServerProfiles()
    .then(function(profiles) {
      if (profiles.defaultServer !== undefined) {
        console.log('\n' + externalizedStrings.infoCmdServerProfiles);
        printAllServersInfo(profiles);
      }
    });
};

function printAllServersInfo(profiles) {
// Get default server
  var defaultServer = profiles.defaultServer,
    urlLabel = externalizedStrings.serverListBaseURLHeaderTitle,
    nameLabel = externalizedStrings.serverListProfileNameHeaderTitle;

  // Get longest strings, to format properly
  var longestURLLength = urlLabel.length;
  var longestServerNameLength = nameLabel.length;

  var server, i;
  var defaultLabel = externalizedStrings.serverListDefaultDescription;

  for (i in profiles.servers) {
    if (i.length > longestServerNameLength) {
      longestServerNameLength = i.length;
    }
    server = profiles.servers[i];
    var url;
    if(typeof server.path !== 'undefined' && server.path.length > 1)
      url = urlFormatter.format({protocol:(server.protocol || 'http') ,hostname:server.host,port:server.port,pathname:server.path});
    else
      url = urlFormatter.format({protocol:(server.protocol || 'http') ,hostname:server.host,port:server.port});

    if (url.length > longestURLLength) {
      longestURLLength = url.length;
    }
  }

  var paddingSize = 2;
  // Some beautiful extra padding for every column
  longestServerNameLength += paddingSize;
  longestURLLength += paddingSize;

  // Pad the header elements
  var padding = ' ';
  while (nameLabel.length < longestServerNameLength) {
    nameLabel += ' ';
  }

  // Create a dash division line (--------------)
  var dashDivision = '';
  for (i = 0; i <= longestURLLength + longestServerNameLength + defaultLabel.length; ++i) {
    dashDivision += '-';
  }

  //Print header
  console.log();
  strings.info(nameLabel + urlLabel);
  strings.info(dashDivision);

  // Pad every element in the server list and print it out
  for (i in profiles.servers) {
    var serverCurrentName = i;
    server = profiles.servers[i];
    
    var serverCurrentUrl;
    if(typeof server.path !== 'undefined' && server.path.length > 1)
      serverCurrentUrl = urlFormatter.format({protocol:(server.protocol || 'http') , hostname: server.host, port: server.port, pathname: server.path});
    else
      serverCurrentUrl = urlFormatter.format({protocol:(server.protocol || 'http') , hostname: server.host, port: server.port});

    while (serverCurrentName.length < longestServerNameLength) {
      serverCurrentName += padding;
    }
    while (serverCurrentUrl.length < longestURLLength) {
      serverCurrentUrl += padding;
    }
    if (defaultServer === i) {
      serverCurrentUrl += defaultLabel;
    }
    console.log(serverCurrentName + serverCurrentUrl);
  }
  strings.info(dashDivision);
}

function getServerDetails(args, profiles) {
  var deferred = Q.defer();

  var serverName = args.argv.remain.shift();
  var server = profiles.servers[serverName];

  if (!profiles.servers.hasOwnProperty(serverName)) {
    var SVRDOESNOTEXISTS = errors.getError('SVRDOESNOTEXISTS', serverName);
    deferred.reject(SVRDOESNOTEXISTS);
  }
  else {
    inquirer.prompt([
      {
        name: 'password',
        message: externalizedStrings.whatServerPassword,
        type: 'password',
        when: function () {
          return (args.password === undefined &&
          (!server.hasOwnProperty('password') || server.password === ''));
        }
      }],
      function (answers) {
        var password;
        if (answers.password !== undefined) {
          password = answers.password;
        }
        else {
          password = args.password || new Buffer(server.password, 'base64');
        }

        var options = {
          protocol: server.protocol,
          host: server.host,
          port: server.port,
          userName: server.login,
          userPassword: password,
          contextRoot: server.contextRoot,
          timeout: server.timeout
        };

        if(typeof server.path !== 'undefined' && server.path.length > 1)
          options["path"] = server.path;

        var isDefault = profiles.defaultServer === serverName ? true : false;
        printServerInformation(serverName, options, isDefault)
          .then(function () {
            deferred.resolve();
          })
          .fail(function (err) {
            deferred.reject(err);
        });
      }
    );
  }
  return deferred.promise;
}

function printServerInformation(serverName, options, isDefault) {
  var deferred = Q.defer();

  console.log(strings.format(externalizedStrings.serverInfoConnecting, serverName));
  serverSupport.getRuntimes(options)
    .then(function (results) {
      var url;
      if(typeof options.path !== undefined && options.path.length > 1)
        url =  urlFormatter.format({protocol:(options.protocol || 'http') , hostname: options.host, port: options.port, pathname: options.path});
      else
        url =  urlFormatter.format({protocol:(options.protocol || 'http') , hostname: options.host, port: options.port});
      // Print initial server information
      console.log(strings.format(externalizedStrings.serverInfoProfileName, serverName));
      console.log(strings.format(externalizedStrings.serverInfoURL, url));
      console.log(strings.format(externalizedStrings.serverInfoVersion, results.productVersion));
      if (isDefault) {
        console.log(externalizedStrings.serverInfoIsDefault);
      } else {
        console.log(externalizedStrings.serverInfoIsNotDefault);
      }

      var runtimes = [];
      for (var i = 0; i < results.projects.length; ++i) {
        runtimes.push(results.projects[i].name);
      }

      printRuntimeInformation(serverName, runtimes, 0, options)
        .then(function () {
          deferred.resolve();
        })
        .fail(function (err) {
          var url;
          if(typeof options.path !== 'undefined' && options.path.length > 1)
            url =  urlFormatter.format({protocol:(options.protocol || 'http') , hostname: options.host, port: options.port, pathname: options.path});
          else
            url =  urlFormatter.format({protocol:(options.protocol || 'http') , hostname: options.host, port: options.port});
          var runtimeFail = errors.getError('SVRUNREACHABLE', serverName, url);
          err.errno = runtimeFail.errno;
          err.description = runtimeFail.description + '\n' + 'Reason: ' + err.message;
          deferred.reject(err);
        });
      })
    .fail(function (err) {
      var url;
      if(typeof options.path !== 'undefined' && options.path.length > 1)
        url =  urlFormatter.format({protocol:(options.protocol || 'http') , hostname: options.host, port: options.port, pathname: options.path});
      else
        url =  urlFormatter.format({protocol:(options.protocol || 'http') , hostname: options.host, port: options.port});      var runtimeFail = errors.getError('SVRUNREACHABLE', serverName, url);
      err.errno = runtimeFail.errno;
      err.description = runtimeFail.description + '\n' + 'Reason: ' + err.message;
      deferred.reject(err);
    });

  return deferred.promise;
}

function printRuntimeInformation(serverName, runtimes, index, options) {
  var deferred = Q.defer();

  var dashLine = '------------------------------------------------------';
  // Nothing else to do, we reach the end of the runtimes list
  if (index === runtimes.length) {
    console.log(dashLine);
    deferred.resolve();
  }
  else {
    console.log(dashLine);
    console.log(strings.format(externalizedStrings.serverInfoRuntime, runtimes[index]));

    options.runtime = runtimes[index];

    printApplicationsInformation(options)
      .then(function () {
        return printAdaptersInformation(options);
      })
      .then(function () {
        // Invoking recursively to print the next runtime information
        return printRuntimeInformation(serverName, runtimes, index + 1, options);
      })
      .then(function () {
        deferred.resolve();
      })
      .catch(function(err){
        deferred.reject(err);
      });

  }
  return deferred.promise;
}

function printApplicationsInformation(options) {
  var deferred = Q.defer();

  serverSupport.getApplications(options)
    .then(function (results) {
      var applications = results.items;
      if (applications.length > 0) {
        console.log(externalizedStrings.serverInfoBoundApps);
      }

      var nativeOSNameMap = {
        'ios': 'iOS',
        'android': 'Android',
        'windowsphone8': 'Windows Phone 8.1',
        'windows': 'Windows 10 UWP',
        'windows8': 'Windows 8.1'
      };

      for (var i = 0; i < applications.length; ++i) {
        var appString = '\t' + applications[i].displayName;
        var environmentsArray = applications[i].envs;
        var environmentsPerVersion = {};

        var j, k;
        for (j = 0; j < environmentsArray.length; ++j) {
          var environment = environmentsArray[j].environment;
          var versionsArray = environmentsArray[j].versions;

          for (k = 0; k < versionsArray.length; ++k) {
            if (environmentsPerVersion[versionsArray[k].version] === undefined) {
              environmentsPerVersion[versionsArray[k].version] = [environment];
            } else {
              environmentsPerVersion[versionsArray[k].version].push(environment);
            }
          }
        }

        //  We may have environments in any order, so re-iterate but now on the environments per version
        var versions = Object.keys(environmentsPerVersion);
        for (j = 0; j < versions.length; ++j) {
          appString += ', v' + versions[j] + ' (';
          var appType = '';
          var environmentsOnThisVersion = environmentsPerVersion[versions[j]]; // This is an array
            for (k = 0; k < environmentsOnThisVersion.length; ++k) {
              appType = nativeOSNameMap[environmentsOnThisVersion[k]];
              if (k === 0) {
                appString += appType;
              }
              else {
                appString += ', ' + appType;
              }
            }
          appString += ')';
        }
        console.log(appString);
      }
      deferred.resolve();
    })
    .fail(function (error) {
      deferred.reject(error);
    });

  return deferred.promise;
}

function printAdaptersInformation(options) {
  var deferred = Q.defer();

  serverSupport.getAdapters(options)
    .then(function (results) {
      var adapters = results.items;
      if (adapters.length > 0) {
        console.log(externalizedStrings.serverInfoAdapters);
      }
      for (var i = 0; i < adapters.length; ++i) {
        console.log('\t' + adapters[i].name);
      }
      deferred.resolve();
    })
    .fail(function (error) {
      deferred.reject(error);
    });

  return deferred.promise;
}
