text: "use strict"
  
  var writeIEEE754 = require('../float_parser').writeIEEE754,
  	readIEEE754 = require('../float_parser').readIEEE754,
  	Long = require('../long').Long,
    Double = require('../double').Double,
    Timestamp = require('../timestamp').Timestamp,
    ObjectID = require('../objectid').ObjectID,
    Symbol = require('../symbol').Symbol,
    Code = require('../code').Code,
    MinKey = require('../min_key').MinKey,
    MaxKey = require('../max_key').MaxKey,
    DBRef = require('../db_ref').DBRef,
    BSONRegExp = require('../regexp').BSONRegExp,
    Binary = require('../binary').Binary;
  
  var BSON = {};
  
  /**
   * Contains the function cache if we have that enable to allow for avoiding the eval step on each deserialization, comparison is by md5
   *
   * @ignore
   * @api private
   */
  var functionCache = BSON.functionCache = {};
  
  /**
   * Number BSON Type
   *
   * @classconstant BSON_DATA_NUMBER
   **/
  BSON.BSON_DATA_NUMBER = 1;
  /**
   * String BSON Type
   *
   * @classconstant BSON_DATA_STRING
   **/
  BSON.BSON_DATA_STRING = 2;
  /**
   * Object BSON Type
   *
   * @classconstant BSON_DATA_OBJECT
   **/
  BSON.BSON_DATA_OBJECT = 3;
  /**
   * Array BSON Type
   *
   * @classconstant BSON_DATA_ARRAY
   **/
  BSON.BSON_DATA_ARRAY = 4;
  /**
   * Binary BSON Type
   *
   * @classconstant BSON_DATA_BINARY
   **/
  BSON.BSON_DATA_BINARY = 5;
  /**
   * ObjectID BSON Type
   *
   * @classconstant BSON_DATA_OID
   **/
  BSON.BSON_DATA_OID = 7;
  /**
   * Boolean BSON Type
   *
   * @classconstant BSON_DATA_BOOLEAN
   **/
  BSON.BSON_DATA_BOOLEAN = 8;
  /**
   * Date BSON Type
   *
   * @classconstant BSON_DATA_DATE
   **/
  BSON.BSON_DATA_DATE = 9;
  /**
   * null BSON Type
   *
   * @classconstant BSON_DATA_NULL
   **/
  BSON.BSON_DATA_NULL = 10;
  /**
   * RegExp BSON Type
   *
   * @classconstant BSON_DATA_REGEXP
   **/
  BSON.BSON_DATA_REGEXP = 11;
  /**
   * Code BSON Type
   *
   * @classconstant BSON_DATA_CODE
   **/
  BSON.BSON_DATA_CODE = 13;
  /**
   * Symbol BSON Type
   *
   * @classconstant BSON_DATA_SYMBOL
   **/
  BSON.BSON_DATA_SYMBOL = 14;
  /**
   * Code with Scope BSON Type
   *
   * @classconstant BSON_DATA_CODE_W_SCOPE
   **/
  BSON.BSON_DATA_CODE_W_SCOPE = 15;
  /**
   * 32 bit Integer BSON Type
   *
   * @classconstant BSON_DATA_INT
   **/
  BSON.BSON_DATA_INT = 16;
  /**
   * Timestamp BSON Type
   *
   * @classconstant BSON_DATA_TIMESTAMP
   **/
  BSON.BSON_DATA_TIMESTAMP = 17;
  /**
   * Long BSON Type
   *
   * @classconstant BSON_DATA_LONG
   **/
  BSON.BSON_DATA_LONG = 18;
  /**
   * MinKey BSON Type
   *
   * @classconstant BSON_DATA_MIN_KEY
   **/
  BSON.BSON_DATA_MIN_KEY = 0xff;
  /**
   * MaxKey BSON Type
   *
   * @classconstant BSON_DATA_MAX_KEY
   **/
  BSON.BSON_DATA_MAX_KEY = 0x7f;
  
  /**
   * Binary Default Type
   *
   * @classconstant BSON_BINARY_SUBTYPE_DEFAULT
   **/
  BSON.BSON_BINARY_SUBTYPE_DEFAULT = 0;
  /**
   * Binary Function Type
   *
   * @classconstant BSON_BINARY_SUBTYPE_FUNCTION
   **/
  BSON.BSON_BINARY_SUBTYPE_FUNCTION = 1;
  /**
   * Binary Byte Array Type
   *
   * @classconstant BSON_BINARY_SUBTYPE_BYTE_ARRAY
   **/
  BSON.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2;
  /**
   * Binary UUID Type
   *
   * @classconstant BSON_BINARY_SUBTYPE_UUID
   **/
  BSON.BSON_BINARY_SUBTYPE_UUID = 3;
  /**
   * Binary MD5 Type
   *
   * @classconstant BSON_BINARY_SUBTYPE_MD5
   **/
  BSON.BSON_BINARY_SUBTYPE_MD5 = 4;
  /**
   * Binary User Defined Type
   *
   * @classconstant BSON_BINARY_SUBTYPE_USER_DEFINED
   **/
  BSON.BSON_BINARY_SUBTYPE_USER_DEFINED = 128;
  
  // BSON MAX VALUES
  BSON.BSON_INT32_MAX = 0x7FFFFFFF;
  BSON.BSON_INT32_MIN = -0x80000000;
  
  BSON.BSON_INT64_MAX = Math.pow(2, 63) - 1;
  BSON.BSON_INT64_MIN = -Math.pow(2, 63);
  
  // JS MAX PRECISE VALUES
  BSON.JS_INT_MAX = 0x20000000000000;  // Any integer up to 2^53 can be precisely represented by a double.
  BSON.JS_INT_MIN = -0x20000000000000;  // Any integer down to -2^53 can be precisely represented by a double.
  
  // Internal long versions
  var JS_INT_MAX_LONG = Long.fromNumber(0x20000000000000);  // Any integer up to 2^53 can be precisely represented by a double.
  var JS_INT_MIN_LONG = Long.fromNumber(-0x20000000000000);  // Any integer down to -2^53 can be precisely represented by a double.
  
  var deserialize = function(buffer, options, isArray) {
  	var index = 0;
  	// Read the document size
    var size = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
  
  	// Ensure buffer is valid size
    if(size < 5 || buffer.length < size) {
  		throw new Error("corrupt bson message");
  	}
  
  	// Illegal end value
  	if(buffer[size - 1] != 0) {
  		throw new Error("One object, sized correctly, with a spot for an EOO, but the EOO isn't 0x00");
  	}
  
  	// Start deserializtion
  	return deserializeObject(buffer, options, isArray);
  }
  
  // // Reads in a C style string
  // var readCStyleStringSpecial = function(buffer, index) {
  // 	// Get the start search index
  // 	var i = index;
  // 	// Locate the end of the c string
  // 	while(buffer[i] !== 0x00 && i < buffer.length) {
  // 		i++
  // 	}
  // 	// If are at the end of the buffer there is a problem with the document
  // 	if(i >= buffer.length) throw new Error("Bad BSON Document: illegal CString")
  // 	// Grab utf8 encoded string
  // 	var string = buffer.toString('utf8', index, i);
  // 	// Update index position
  // 	index = i + 1;
  // 	// Return string
  // 	return {s: string, i: index};
  // }
  
  // Reads in a C style string
  var readCStyleStringSpecial = function(buffer, index) {
  	// Get the start search index
  	var i = index;
  	// Locate the end of the c string
  	while(buffer[i] !== 0x00 && i < buffer.length) {
  		i++
  	}
  	// If are at the end of the buffer there is a problem with the document
  	if(i >= buffer.length) throw new Error("Bad BSON Document: illegal CString")
  	// Grab utf8 encoded string
  	return buffer.toString('utf8', index, i);
  }
  
  var DeserializationMethods = {}
  DeserializationMethods[BSON.BSON_DATA_OID] = function(name, object, buffer, index) {
  	var string = buffer.toString('binary', index, index + 12);
  	object[name] = new ObjectID(string);
  	return index + 12;
  }
  
  DeserializationMethods[BSON.BSON_DATA_NUMBER] = function(name, object, buffer, index) {
  	object[name] = buffer.readDoubleLE(index);
    return index + 8;
  }
  
  DeserializationMethods[BSON.BSON_DATA_INT] = function(name, object, buffer, index) {
  	object[name] = buffer.readInt32LE(index);
  	return index + 4;
  }
  
  DeserializationMethods[BSON.BSON_DATA_TIMESTAMP] = function(name, object, buffer, index) {
    var lowBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
    var highBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
    object[name] = new Timestamp(lowBits, highBits);
  	return index;
  }
  
  DeserializationMethods[BSON.BSON_DATA_STRING] = function(name, object, buffer, index) {
    var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
  	if(stringSize <= 0 || stringSize > (buffer.length - index) || buffer[index + stringSize - 1] != 0) throw new Error("bad string length in bson");
    object[name] = buffer.toString('utf8', index, index + stringSize - 1);
    return index + stringSize;
  }
  
  DeserializationMethods[BSON.BSON_DATA_BOOLEAN] = function(name, object, buffer, index) {
    object[name] = buffer[index++] == 1;
  	return index;
  }
  
  var deserializeObject = function(buffer, options, isArray) {
    // Options
    options = options == null ? {} : options;
    var evalFunctions = options['evalFunctions'] == null ? false : options['evalFunctions'];
    var cacheFunctions = options['cacheFunctions'] == null ? false : options['cacheFunctions'];
    var cacheFunctionsCrc32 = options['cacheFunctionsCrc32'] == null ? false : options['cacheFunctionsCrc32'];
    var promoteLongs = options['promoteLongs'] == null ? true : options['promoteLongs'];
  	var fieldsAsRaw = options['fieldsAsRaw'] == null ? {} : options['fieldsAsRaw'];
    // Return BSONRegExp objects instead of native regular expressions
    var bsonRegExp = typeof options['bsonRegExp'] == 'boolean' ? options['bsonRegExp'] : false;
    var promoteBuffers = options['promoteBuffers'] == null ? false : options['promoteBuffers'];
  
    // Validate that we have at least 4 bytes of buffer
    if(buffer.length < 5) throw new Error("corrupt bson message < 5 bytes long");
  
    // Set up index
    var index = typeof options['index'] == 'number' ? options['index'] : 0;
  
  	// Read the document size
    var size = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
  
  	// Ensure buffer is valid size
    if(size < 5 || size > buffer.length) throw new Error("corrupt bson message");
  
    // Create holding object
    var object = isArray ? [] : {};
  
    // While we have more left data left keep parsing
    while(true) {
      // Read the type
      var elementType = buffer[index++];
      // If we get a zero it's the last byte, exit
      if(elementType == 0) break;
      var name = readCStyleStringSpecial(buffer, index);
  		index = index + name.length + 1;
  
  		// console.log("----------- 0 " + elementType + " :: " + name)
  		index = DeserializationMethods[elementType](name, object, buffer, index);
  		// console.log('--------------- 1')
    }
  
    // Check if we have a db ref object
    if(object['$id'] != null) object = new DBRef(object['$ref'], object['$id'], object['$db']);
  
    // Return the final objects
    return object;
  }
  
  /**
   * Ensure eval is isolated.
   *
   * @ignore
   * @api private
   */
  var isolateEvalWithHash = function(functionCache, hash, functionString, object) {
    // Contains the value we are going to set
    var value = null;
  
    // Check for cache hit, eval if missing and return cached function
    if(functionCache[hash] == null) {
      eval("value = " + functionString);
      functionCache[hash] = value;
    }
    // Set the object
    return functionCache[hash].bind(object);
  }
  
  /**
   * Ensure eval is isolated.
   *
   * @ignore
   * @api private
   */
  var isolateEval = function(functionString) {
    // Contains the value we are going to set
    var value = null;
    // Eval the function
    eval("value = " + functionString);
    return value;
  }
  
  module.exports = deserialize
  