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

var path = require('path');

var dashdash = require('dashdash');

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


///--- Globals

dashdash.addOptionType({
  name: 'ldap.DN',
  takesArg: true,
  helpArg: 'LDAP_DN',
  parseArg: function (option, optstr, arg) {
    return ldap.parseDN(arg);
  }
});

dashdash.addOptionType({
  name: 'ldap.Filter',
  takesArg: true,
  helpArg: 'LDAP_FILTER',
  parseArg: function (option, optstr, arg) {
    return ldap.parseFilter(arg);
  }
});

dashdash.addOptionType({
  name: 'ldap.scope',
  takesArg: true,
  helpArg: 'SCOPE',
  parseArg: function (option, optstr, arg) {
    if (!/^base|one|sub$/.test(arg)) {
      throw new TypeError('Scope must be <base|one|sub>');
    }
    return arg;
  }
});


var opts = [
  {
    names: ['base', 'b'],
    type: 'ldap.DN',
    help: 'Base DN of search',
    default: ''
  },
  {
    names: ['scope', 's'],
    type: 'ldap.scope',
    help: 'Search scope <base|sub|one>',
    helpArg: 'SCOPE',
    default: 'sub'
  },
  {
    names: ['timeout', 't'],
    type: 'integer',
    help: 'Search timeout',
    helpArg: 'SECS'
  },
  {
    names: ['persistent', 'p'],
    type: 'bool',
    help: 'Enable persistent search control',
    default: false
  },
  {
    names: ['paged', 'g'],
    type: 'number',
    help: 'Enable paged search result control',
    helpArg: 'PAGE_SIZE'
  },
  {
    names: ['control', 'c'],
    type: 'arrayOfString',
    help: 'Send addition control OID',
    helpArg: 'OID',
    default: []
  },
  { group: 'General Options' },
  {
    names: ['help', 'h'],
    type: 'bool',
    help: 'Print this help and exit.'
  },
  {
    names: ['debug', 'd'],
    type: 'integer',
    help: 'Set debug level <0-2>',
    helpArg: 'LEVEL'
  },
  { group: 'Connection Options' },
  {
    names: ['url', 'u'],
    type: 'string',
    help: 'LDAP server URL',
    helpArg: 'URL',
    default: 'ldap://127.0.0.1:389'
  },
  {
    names: ['binddn', 'D'],
    type: 'ldap.DN',
    help: 'Bind DN',
    default: ''
  },
  {
    names: ['password', 'w'],
    type: 'string',
    help: 'Bind password',
    helpArg: 'PASSWD',
    default: ''
  },
  {
    names: ['insecure', 'i'],
    type: 'bool',
    env: 'LDAPJS_TLS_INSECURE',
    help: 'Disable SSL certificate verification',
    default: false
  }
];
var parser = dashdash.createParser({options: opts});



///--- Helpers

function usage(code, message) {
  var msg = (message ? message + '\n' : '') +
    'Usage: ' + path.basename(process.argv[1]) +
      ' [OPTIONS] FILTER [ATTRIBUTES...]\n\n' +
    parser.help({includeEnv: true});

  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 = parser.parse(process.argv);
} catch (e) {
  usage(1, e.toString());
}

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

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

var logLevel = 'info';

if (parsed.debug)
  logLevel = (parsed.debug > 1 ? 'trace' : 'debug');

var log = new Logger({
    name: 'ldapjs',
    component: 'client',
    stream: process.stderr,
    level: logLevel
});

var client = ldap.createClient({
  url: parsed.url,
  log: log,
  timeout: parsed.timeout || false,
  tlsOptions: {
    rejectUnauthorized: !parsed.insecure
  }
});

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);
  }
  var req = {
    scope: parsed.scope || 'sub',
    filter: parsed._args[0],
    attributes: parsed._args.length > 1 ? parsed._args.slice(1) : []
  };
  if (parsed.paged) {
    req.paged = {
      pageSize: parsed.paged
    };
  }
  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;
      });
    });
  });
});