/*
 * Copyright 2012-2016 the original author or authors
 * @license MIT, see LICENSE.txt for details
 *
 * @author Jeremy Grelle
 * @author Scott Andrews
 */

'use strict';

var parser, http, https, mixin, normalizeHeaderName, responsePromise, client, httpsExp;

parser = require('url');
http = require('http');
https = require('https');
mixin = require('../util/mixin');
normalizeHeaderName = require('../util/normalizeHeaderName');
responsePromise = require('../util/responsePromise');
client = require('../client');

httpsExp = /^https/i;

// TODO remove once Node 0.6 is no longer supported
Buffer.concat = Buffer.concat || function (list, length) {
	/*jshint plusplus:false, shadow:true */
	// from https://github.com/joyent/node/blob/v0.8.21/lib/buffer.js
	if (!Array.isArray(list)) {
		throw new Error('Usage: Buffer.concat(list, [length])');
	}

	if (list.length === 0) {
		return new Buffer(0);
	} else if (list.length === 1) {
		return list[0];
	}

	if (typeof length !== 'number') {
		length = 0;
		for (var i = 0; i < list.length; i++) {
			var buf = list[i];
			length += buf.length;
		}
	}

	var buffer = new Buffer(length);
	var pos = 0;
	for (var i = 0; i < list.length; i++) {
		var buf = list[i];
		buf.copy(buffer, pos);
		pos += buf.length;
	}
	return buffer;
};

module.exports = client(function node(request) {
	/*jshint maxcomplexity:20 */
	return responsePromise.promise(function (resolve, reject) {

		var options, clientRequest, client, url, headers, entity, response;

		request = typeof request === 'string' ? { path: request } : request || {};
		response = { request: request };

		if (request.canceled) {
			response.error = 'precanceled';
			reject(response);
			return;
		}

		url = response.url = request.path || '';
		client = url.match(httpsExp) ? https : http;

		options = mixin({}, request.mixin, parser.parse(url));

		entity = request.entity;
		request.method = request.method || (entity ? 'POST' : 'GET');
		options.method = request.method;
		headers = options.headers = {};
		Object.keys(request.headers || {}).forEach(function (name) {
			headers[normalizeHeaderName(name)] = request.headers[name];
		});
		if (!headers['Content-Length']) {
			headers['Content-Length'] = entity ? Buffer.byteLength(entity, 'utf8') : 0;
		}

		request.canceled = false;
		request.cancel = function cancel() {
			request.canceled = true;
			clientRequest.abort();
		};

		clientRequest = client.request(options, function (clientResponse) {
			// Array of Buffers to collect response chunks
			var buffers = [];

			response.raw = {
				request: clientRequest,
				response: clientResponse
			};
			response.status = {
				code: clientResponse.statusCode
				// node doesn't provide access to the status text
			};
			response.headers = {};
			Object.keys(clientResponse.headers).forEach(function (name) {
				response.headers[normalizeHeaderName(name)] = clientResponse.headers[name];
			});

			clientResponse.on('data', function (data) {
				// Collect the next Buffer chunk
				buffers.push(data);
			});

			clientResponse.on('end', function () {
				// Create the final response entity
				response.entity = buffers.length > 0 ? Buffer.concat(buffers).toString() : '';
				buffers = null;

				resolve(response);
			});
		});

		clientRequest.on('error', function (e) {
			response.error = e;
			reject(response);
		});

		if (entity) {
			clientRequest.write(entity);
		}
		clientRequest.end();

	});
});
