// Copyright 2011 Mark Cavage, Inc.  All rights reserved.

var assert = require('assert');
var util = require('util');

var asn1 = require('asn1');

var LDAPMessage = require('./message');
var LDAPResult = require('./result');
var dn = require('../dn');
var filters = require('../filters');
var Protocol = require('../protocol');



///--- Globals

var DN = dn.DN;
var Ber = asn1.Ber;


///--- API

function SearchRequest(options) {
  if (options) {
    if (typeof (options) !== 'object')
      throw new TypeError('options must be an object');
  } else {
    options = {};
  }

  options.protocolOp = Protocol.LDAP_REQ_SEARCH;
  LDAPMessage.call(this, options);

  var self = this;
  this.__defineGetter__('type', function () { return 'SearchRequest'; });
  this.__defineGetter__('_dn', function () { return self.baseObject; });
  this.__defineGetter__('scope', function () {
    switch (self._scope) {
    case Protocol.SCOPE_BASE_OBJECT: return 'base';
    case Protocol.SCOPE_ONE_LEVEL: return 'one';
    case Protocol.SCOPE_SUBTREE: return 'sub';
    default:
      throw new Error(self._scope + ' is an invalid search scope');
    }
  });
  this.__defineSetter__('scope', function (s) {
    if (typeof (s) === 'string') {
      switch (s) {
      case 'base':
        self._scope = Protocol.SCOPE_BASE_OBJECT;
        break;
      case 'one':
        self._scope = Protocol.SCOPE_ONE_LEVEL;
        break;
      case 'sub':
        self._scope = Protocol.SCOPE_SUBTREE;
        break;
      default:
        throw new Error(s + ' is an invalid search scope');
      }
    } else {
      self._scope = s;
    }
  });

  if (options.baseObject !== undefined) {
    this.baseObject = options.baseObject;
  } else {
    this.baseObject = dn.parse('');
  }
  this.scope = options.scope || 'base';
  this.derefAliases = options.derefAliases || Protocol.NEVER_DEREF_ALIASES;
  this.sizeLimit = options.sizeLimit || 0;
  this.timeLimit = options.timeLimit || 0;
  this.typesOnly = options.typesOnly || false;
  this.filter = options.filter || null;
  this.attributes = options.attributes ? options.attributes.slice(0) : [];
}
util.inherits(SearchRequest, LDAPMessage);
module.exports = SearchRequest;


SearchRequest.prototype.newResult = function () {
  var self = this;

  return new LDAPResult({
    messageID: self.messageID,
    protocolOp: Protocol.LDAP_REP_SEARCH
  });
};


SearchRequest.prototype._parse = function (ber) {
  assert.ok(ber);

  this.baseObject = ber.readString();
  this.scope = ber.readEnumeration();
  this.derefAliases = ber.readEnumeration();
  this.sizeLimit = ber.readInt();
  this.timeLimit = ber.readInt();
  this.typesOnly = ber.readBoolean();

  this.filter = filters.parse(ber);

  // look for attributes
  if (ber.peek() === 0x30) {
    ber.readSequence();
    var end = ber.offset + ber.length;
    while (ber.offset < end)
      this.attributes.push(ber.readString().toLowerCase());
  }

  return true;
};


SearchRequest.prototype._toBer = function (ber) {
  assert.ok(ber);

  ber.writeString(this.baseObject.toString());
  ber.writeEnumeration(this._scope);
  ber.writeEnumeration(this.derefAliases);
  ber.writeInt(this.sizeLimit);
  ber.writeInt(this.timeLimit);
  ber.writeBoolean(this.typesOnly);

  var f = this.filter || new filters.PresenceFilter({attribute: 'objectclass'});
  ber = f.toBer(ber);

  ber.startSequence(Ber.Sequence | Ber.Constructor);
  if (this.attributes && this.attributes.length) {
    this.attributes.forEach(function (a) {
      ber.writeString(a);
    });
  }
  ber.endSequence();

  return ber;
};


SearchRequest.prototype._json = function (j) {
  assert.ok(j);

  j.baseObject = this.baseObject;
  j.scope = this.scope;
  j.derefAliases = this.derefAliases;
  j.sizeLimit = this.sizeLimit;
  j.timeLimit = this.timeLimit;
  j.typesOnly = this.typesOnly;
  j.filter = this.filter.toString();
  j.attributes = this.attributes;

  return j;
};
