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

var assert = require('assert-plus');
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 Ber = asn1.Ber;


///--- API

function SearchRequest(options) {
  options = options || {};
  assert.object(options);

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

  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);
Object.defineProperties(SearchRequest.prototype, {
  type: {
    get: function getType() { return 'SearchRequest'; },
    configurable: false
  },
  _dn: {
    get: function getDN() { return this.baseObject; },
    configurable: false
  },
  scope: {
    get: function getScope() {
      switch (this._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(this._scope + ' is an invalid search scope');
      }
    },
    set: function setScope(val) {
      if (typeof (val) === 'string') {
        switch (val) {
        case 'base':
          this._scope = Protocol.SCOPE_BASE_OBJECT;
          break;
        case 'one':
          this._scope = Protocol.SCOPE_ONE_LEVEL;
          break;
        case 'sub':
          this._scope = Protocol.SCOPE_SUBTREE;
          break;
        default:
          throw new Error(val + ' is an invalid search scope');
        }
      } else {
        this._scope = val;
      }
    },
    configurable: false
  }
});

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;
};


///--- Exports

module.exports = SearchRequest;
