/*
	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 moduleName = 'get-file';

//private modules
var errors = require('mdo-errors');
var messages = require('../externalizedStrings');
var strings = require('ibm-strings');
var getCertificate = require('../rest-client-api/get-certificate.js').getCertificate;

//3rd party modules
var log = require('npmlog');
var Q = require('q');
var FormData = require('form-data');
var http = require('http');
var Verror   = require('verror');

var getFile = function (host, port, path, protocol, userName, password, timeout, filePath) {

  var d = Q.defer();

  var argsErr = null;
  if (typeof host === 'undefined' || host === null) {
    argsErr = new Error(strings.format(messages.paramNotDefined, 'host'));
  } else if (typeof port === 'undefined' || port === null) {
    argsErr = new Error(strings.format(messages.paramNotDefined, 'port'));
  } else if (typeof path === 'undefined' || path === null) {
    argsErr = new Error(strings.format(messages.paramNotDefined, 'path'));
  } else if (typeof protocol === 'undefined' || protocol === null) {
    argsErr = new Error(strings.format(messages.paramNotDefined, 'protocol'));
  } else if (typeof userName === 'undefined' || userName === null) {
    argsErr = new Error(strings.format(messages.paramNotDefined, 'userName'));
  } else if (typeof password === 'undefined' || password === null) {
    argsErr = new Error(strings.format(messages.paramNotDefined, 'password'));
  } else if (typeof timeout === 'undefined' || timeout === null) {
    argsErr = new Error(strings.format(messages.paramNotDefined, 'timeout'));
  }  else if (typeof filePath === 'undefined' || filePath === null) {
    argsErr = new Error(strings.format(messages.paramNotDefined, 'filePath'));
  }
  if (argsErr !== null) {
    argsErr.name = 'InvalidArgumentsError';
    d.reject(argsErr);
    return d.promise;
  }

  var headers = {
    'Authorization': 'Basic ' + new Buffer(userName + ':' + password).toString('base64')
  };

  var http_options = {
    host: host,
    port: port,
    path: path,
    method: 'GET',
    protocol: protocol,
    headers:headers
  };

  getCertificate(http_options, moduleName);

  var form = new FormData();
// Need this to catch file read/transmit issues
  form.on('error', function(err) {
    d.reject(err);
  });


  try {


    var fs = require('fs');
    var writeStream = fs.createWriteStream(filePath);

    writeStream.on('open', function() {

      log.silly(moduleName, 'form.submit httpoptions: %j', http_options);
      var req = form.submit(http_options, function(err, resp) {  //jshint ignore:line

        var body = '{}';

        // before getting started we need to verify that the filePath provide is an actual file path (not dir, etc...)
        var path = require('path');
        var filePathParent = path.dirname(filePath);
        try {
          if (!fs.existsSync(filePathParent)) {
            log.verbose(moduleName, 'filepath parent dir does not point to a valid file location!');
            var err = new Error("file path parent dir does not point to a valid file location");
            err.name = 'InvalidArgumentsError';
            d.reject(err);
            return d.promise;
          }
        } catch (err) {
          d.reject(err);
          return d.promise;
        }

        // Error passed if target server connection cannot be made
        if (err) {  // err = Error object passed to req.on('error') callback
          d.reject(err);
        }
        else {
          if (resp) {

            log.silly(moduleName, 'response output: '+resp);
            var resperr = null;
            // For possible later use, create a common HTTP error object
            var httperr = new Error('HTTP '+resp.statusCode+' - '+http.STATUS_CODES[resp.statusCode]);
            httperr.name = http.STATUS_CODES[resp.statusCode].replace(/ /g, '');

            // write body of response to file
            resp.pipe(writeStream);

            resp.on('close', function() { //readable stream event; may fire when underlying connection is closed.

              log.silly(moduleName, 'resp.close: resp connection closed.  status: '+resp.statusCode);

              writeStream.end();

              if(resp.statusCode !== 200) {
                if (fs.existsSync(filePath)) {
                  try {
                    fs.unlinkSync(filePath);
                  }
                  catch (err) {
                  }
                }
              }

              d.resolve({
                resp: resp,
                body: body,
                httperr: httperr
              });

            });
            resp.on('end', function() { //readable stream event; fires when no [more] data to read

              writeStream.end();

              if(resp.statusCode !== 200) {
                if (fs.existsSync(filePath)) {
                  try {
                    log.silly(moduleName, 'deleting local copy of file: ' + filePath);
                    fs.unlinkSync(filePath);
                    log.verbose(moduleName, 'resource not found on server');
                    d.reject(httperr)
                  }
                  catch (err) {
                  }
                }
              }

              log.silly(moduleName, 'resp.end: resp data completely read.  status: '+resp.statusCode);

              d.resolve({
                resp: resp,
                body: body,
                httperr: httperr
              });

            });
            resp.on('error', function(err) {  //readable stream event; fires when error receiving data.  cb arg = Error object
              if (fs.existsSync(filePath)) {
                try {
                  fs.unlinkSync(filePath);
                }
                catch (err) {
                }
              }
              resperr = new Verror(err, httperr.message);
              resperr.name = httperr.name;
              d.reject(resperr);
            });
          }
          log.silly(moduleName, 'post response: ', resp.statusCode, resp.statusMessage);
        }
      });

      req.on('socket', function(socket) {
        socket.on('connect', function(socket) {  //jshint ignore:line
          log.silly(moduleName, 'socket connect');
        });
        socket.setTimeout(timeout, function() {
          if (fs.existsSync(filePath)) {
            try {
              fs.unlinkSync(filePath);
            }
            catch (err) {
            }
          }
          req.abort();
          log.verbose(moduleName, 'socket timeout!');
          var err = new Error(messages.requestTimeout);
          err.name = 'RequestTimeout';
          d.reject(err);
        });
      });

    });


  }
  catch (e) {
    var exerr = new Error(e.message);
    exerr.name = e.name;
    d.reject(exerr);
  }

  return d.promise;

};

module.exports = {

  getFile: getFile

};
