#!/usr/bin/env node
// -*- mode: js -*-
// Copyright 2011 Mark Cavage.  All rights reserved.

var path = require('path');
var url = require('url');

var nopt = require('nopt');

var ldap = require('../lib/index');



///--- Globals

var log4js = ldap.log4js;

nopt.typeDefs.DN = {
  type: ldap.DN,
  validate: function(data, k, val) {
    data[k] = ldap.parseDN(val);
  }
};

nopt.typeDefs.Filter = {
  type: ldap.Filter,
  validate: function(data, k, val) {
    data[k] = ldap.parseFilter(val);
  }
};


var opts = {
  'debug': Number,
  'base': ldap.DN,
  'binddn': ldap.DN,
  'control': Array,
  'password': String,
  'persistent': Boolean,
  'paged': Number,
  'scope': String,
  'timeout': Number,
  'url': url
};

var shortOpts = {
  'c': ['--control'],
  'd': ['--debug'],
  'b': ['--base'],
  'D': ['--binddn'],
  'w': ['--password'],
  'p': ['--persistent'],
  'g': ['--paged'],
  's': ['--scope'],
  't': ['--timeout'],
  'u': ['--url']
};



///--- Helpers

function usage(code, message) {
  var _opts = '';
  Object.keys(shortOpts).forEach(function(k) {
    if (!Array.isArray(shortOpts[k]))
      return;
    var longOpt = shortOpts[k][0].replace('--', '');
    var type = opts[longOpt].name || 'string';
    if (type && type === 'boolean') type = '';
    type = type.toLowerCase();

    _opts += ' [--' + longOpt + ' ' + type + ']';
  });
  _opts += ' filter [attributes...]';

  var msg = (message ? message + '\n' : '') +
    'usage: ' + path.basename(process.argv[1]) + _opts;

  process.stderr.write(msg + '\n');
  process.exit(code);
}


function perror(err) {
  if (parsed.debug) {
    process.stderr.write(err.stack + '\n');
  } else {
    process.stderr.write(err.message + '\n');
  }
  process.exit(1);
}



///--- Mainline

var parsed;

try {
  parsed = nopt(opts, shortOpts, process.argv, 2);
} catch (e) {
  usage(1, e.toString());
}

if (parsed.help)
  usage(0);
if (parsed.argv.remain.length < 1)
  usage(1, 'filter required');

try {
  ldap.parseFilter(parsed.argv.remain[0]);
} catch (e) {
  usage(1, e.message);
}


if (parsed.debug)
  log4js.setGlobalLogLevel(parsed.debug > 1 ? 'TRACE' : 'DEBUG');
if (!parsed.url)
  parsed.url = 'ldap://127.0.0.1:389';
if (!parsed.binddn)
  parsed.binddn = '';
if (!parsed.password)
  parsed.password = '';
if (!parsed.base)
  parsed.base = '';
if (!parsed.control)
  parsed.control = [];
if (!parsed.persistent)
  parsed.persistent = false;

var client = ldap.createClient({
  url: parsed.url,
  log4js: log4js,
  timeout: parsed.timeout || false
});

client.on('error', function(err) {
  perror(err);
});

client.on('timeout', function(req) {
  process.stderr.write('Timeout reached\n');
  process.exit(1);
});

client.bind(parsed.binddn, parsed.password, function(err, res) {
  if (err)
    perror(err);

  var controls = [];
  parsed.control.forEach(function(c) {
    controls.push(new ldap.Control({
      type: c,
      criticality: true
    }));
  });
  if (parsed.persistent) {
    var pCtrl = new ldap.PersistentSearchControl({
      type: '2.16.840.1.113730.3.4.3',
      value: {
        changeTypes: 15,
        changesOnly: false,
        returnECs: true
      }
    });
    controls.push(pCtrl);
  }
  if (parsed.paged) {
    var ctrl = new ldap.PagedResultsControl({ value: { size: parsed.paged } });
    controls.push(ctrl);
  }
  var req = {
    scope: parsed.scope || 'sub',
    filter: parsed.argv.remain[0],
    attributes: parsed.argv.remain.length > 1 ? parsed.argv.remain.slice(1) : []
  };
  client.search(parsed.base, req, controls, function(err, res) {
    if (err)
      perror(err);

    res.on('searchEntry', function(entry) {
      process.stdout.write(JSON.stringify(entry.object, null, 2) + '\n');
    });
    res.on('error', function(err) {
      perror(err);
    });
    res.on('end', function(res) {
      if (res.status !== 0)
        process.stderr.write(ldap.getMessage(res.status) + '\n');
      client.unbind(function() {
        return;
      });
    });
  });
});