#!/usr/bin/env node

/**
 * Modules
 */

var fs = require('fs');
var url = require('url');
var http = require('http');
var path = require('path');
var Stream = require('stream').Stream;
var StringDecoder = require('string_decoder').StringDecoder;

/**
 * Mozilla Root Cert URL
 */

var certUrl = 'https://raw.githubusercontent.com/joyent/node/master/src/node_root_certs.h';

/**
 * Get Root Certs
 */

function getRootCerts(callback) {
  return request(certUrl, function(err, res, body) {
    if (err) return callback(err);
    body = body.replace(/,$/, '');
    body = 'var RootCerts = [\n' + body + '\n];\n';
    body = body.replace(/^"/gm, '+ "');
    body = body.replace(/^\+ "-----B/gm, '"-----B');
    body += ''
      + '\n'
      + '// Use hash table for efficiency:\n'
      + 'RootCerts = RootCerts.reduce(function(trusted, cert) {\n'
      + '  cert = cert.replace(/\\s+/g, "");\n'
      + '  trusted[cert] = true;\n'
      + '  return trusted;\n'
      + '}, {});\n'
      + '\n'
      + 'function isTrusted(pem) {\n'
      + '  pem = pem + "";\n'
      + '  pem = pem.replace(/\\s+/g, "");\n'
      + '  return !!RootCerts[pem];\n'
      + '}\n'
      + '\n'
      + 'exports = RootCerts;\n'
      + 'exports.isTrusted = isTrusted;\n'
      + 'module.exports = exports;\n';
    return callback(null, body);
  });
}

/**
 * Helpers
 */

function request(options, callback) {
  if (typeof options === 'string' || options.hostname) {
    options = { uri: options };
  }

  var uri = options.uri || options.url
    , body = options.json
        ? JSON.stringify(options.json)
        : options.body || '';

  if (typeof uri !== 'object') {
    uri = url.parse(uri);
  }

  if (options.qs) {
    var query = uri.query ? qs.parse(uri.query) : {};
    Object.keys(options.qs).forEach(function(key) {
      query[key] = options.qs[key];
    });
    uri.path = uri.pathname + '?' + qs.stringify(query);
  }

  var protocol = uri.protocol === 'https:'
    ? require('https')
    : http;

  options.method = options.method || (body ? 'POST' : 'GET');
  options.method = options.method.toUpperCase();
  options.headers = options.headers || {};

  options.headers['Accept'] = options.headers['Accept'] || 'text/plain; charset=utf-8';

  if (options.json) {
    options.headers['Content-Type'] = 'application/json; charset=utf-8';
    options.headers['Accept'] = 'application/json';
  }

  if (options.method !== 'GET' && options.method !== 'HEAD') {
    options.headers['Content-Length'] = Buffer.byteLength(body);
  }

  var opt = {
    auth: uri.auth,
    host: uri.hostname,
    port: uri.port || (protocol === http ? 80 : 443),
    path: uri.path,
    method: options.method,
    headers: options.headers
  };


  var req = protocol.request(opt)
    , response = new Stream;

  req.on('error', function(err) {
    if (callback) {
      callback(err);
    } else {
      response.emit('error', err);
    }
  });

  req.on('response', function(res) {
    var decoder = new StringDecoder('utf8')
      , done = false
      , body = '';

    function end() {
      if (done) return;
      done = true;
      if (callback) {
        res.body = body;
        if (options.json) {
          try {
            body = JSON.parse(body);
          } catch (e) {
            ;
          }
        }
        callback(null, res, body);
      } else {
        response.emit('end');
      }
      res.socket.removeListener('error', error);
      res.socket.removeListener('end', end);
    }

    function error(err) {
      res.destroy();
      if (callback) {
        callback(err);
      } else {
        response.emit('error', err);
      }
    }

    res.on('data', function(data) {
      if (callback) {
        body += decoder.write(data);
      } else {
        response.emit('data', data);
      }
    });

    res.on('error', error);
    res.socket.on('error', error);

    res.on('end', end);
    // An agent socket's `end` sometimes
    // wont be emitted on the response.
    res.socket.on('end', end);
  });

  req.end(body);

  return response;
}

/**
 * Execute
 */

function main(argv, callback) {
  if (!callback) {
    callback = argv;
    argv = null;
  }
  return getRootCerts(function(err, certs) {
    var file = path.resolve(__dirname, '..', 'lib', 'common', 'RootCerts.js');
    return fs.writeFile(file, certs, callback);
  });
}

if (!module.parent) {
  process.title = 'root-certs';
  main(process.argv.slice(), function(err, code) {
    if (err) throw err;
    return process.exit(code || 0);
  });
} else {
  module.exports = main;
}
