#
#
#            Nimrod's Runtime Library
#        (c) Copyright 2012 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## This module is a wrapper for the `mongodb`:idx: client C library.
## It allows you to connect to a mongo-server instance, send commands and
## receive replies.

# 
#    Copyright 2009-2011 10gen Inc.
# 
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
# 

import oids, times

{.deadCodeElim: on.}

when defined(windows):
  const
    mongodll* = "mongoc.dll"
    bsondll* = "bson.dll"
elif defined(macosx):
  const 
    mongodll* = "libmongoc.dylib"
    bsondll* = "libbson.dylib"
else:
  const 
    mongodll* = "libmongoc.so"
    bsondll* = "libbson.so"

#
#  This package supports both compile-time and run-time determination of CPU
#  byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
#  compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
#  defined as non-zero, the code will be compiled to run only on big-endian
#  CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
#  run on either big- or little-endian CPUs, but will run slightly less
#  efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
# 

type 
  Tmd5_state*{.pure, final.} = object 
    count*: array[0..2 - 1, int32] # message length in bits, lsw first 
    abcd*: array[0..4 - 1, int32] # digest buffer 
    buf*: array[0..64 - 1, byte] # accumulate block 
  

proc sock_init*(): cint{.stdcall, importc: "mongo_sock_init", dynlib: mongodll.}
const 
  OK* = 0
  ERROR* = - 1
  SIZE_OVERFLOW* = 1
  defaultHost* = "127.0.0.1"
  defaultPort* = 27017

type 
  TValidity* = enum ## validity
    VALID = 0,                ## BSON is valid and UTF-8 compliant. 
    NOT_UTF8 = (1 shl 1),     ## A key or a string is not valid UTF-8. 
    FIELD_HAS_DOT = (1 shl 2),  ## Warning: key contains '.' character. 
    FIELD_INIT_DOLLAR = (1 shl 3),  ## Warning: key starts with '$' character. 
    ALREADY_FINISHED = (1 shl 4) ## Trying to modify a finished BSON object. 
  TBinarySubtype* = enum 
    BIN_BINARY = 0, BIN_FUNC = 1, BIN_BINARY_OLD = 2, BIN_UUID = 3, BIN_MD5 = 5, 
    BIN_USER = 128
  TBsonKind* {.size: sizeof(cint).} = enum 
    bkEOO = 0, 
    bkDOUBLE = 1, 
    bkSTRING = 2, 
    bkOBJECT = 3, 
    bkARRAY = 4, 
    bkBINDATA = 5, 
    bkUNDEFINED = 6, 
    bkOID = 7, 
    bkBOOL = 8, 
    bkDATE = 9, 
    bkNULL = 10, 
    bkREGEX = 11, 
    bkDBREF = 12,  #*< Deprecated. 
    bkCODE = 13, 
    bkSYMBOL = 14, 
    bkCODEWSCOPE = 15, 
    bkINT = 16, 
    bkTIMESTAMP = 17, 
    bkLONG = 18
  TBsonBool* = cint
  TIter* {.pure, final.} = object 
    cur*: cstring
    first*: TBsonBool

  TBson* {.pure, final.} = object 
    data*: cstring
    cur*: cstring
    dataSize*: cint
    finished*: TBsonBool
    stack*: array[0..32 - 1, cint]
    stackPos*: cint
    err*: cint       ## Bitfield representing errors or warnings on this buffer 
    errstr*: cstring ## A string representation of the most recent error
                     ## or warning. 
  
  TDate* = int64

# milliseconds since epoch UTC 

type
  TTimestamp*{.pure, final.} = object ## a timestamp
    i*: cint                  # increment 
    t*: cint                  # time in seconds 

proc create*(): ptr TBson{.stdcall, importc: "bson_create", dynlib: bsondll.}
proc dispose*(b: ptr TBson){.stdcall, importc: "bson_dispose", dynlib: bsondll.}

proc size*(b: var TBson): cint {.stdcall, importc: "bson_size", dynlib: bsondll.}
  ## Size of a BSON object.

proc bufferSize*(b: var TBson): cint{.stdcall, importc: "bson_buffer_size", 
                                      dynlib: bsondll.}
  ## Buffer size of a BSON object.

proc print*(b: var TBson){.stdcall, importc: "bson_print", dynlib: bsondll.}
  ## Print a string representation of a BSON object.

proc print*(TBson: cstring, depth: cint) {.stdcall, 
    importc: "bson_print_raw", dynlib: bsondll.}
  ## Print a string representation of a BSON object up to `depth`.

proc data*(b: var TBson): cstring{.stdcall, importc: "bson_data", 
                                   dynlib: bsondll.}
  ## Return a pointer to the raw buffer stored by this bson object.

proc find*(it: var TIter, obj: var TBson, name: cstring): TBsonKind {.stdcall, 
    importc: "bson_find", dynlib: bsondll.}
  ## Advance `it` to the named field. `obj` is the BSON object to use.
  ## `name` is the name of the field to find. Returns the type of the found
  ## object or ``bkEOO`` if it is not found.
  
proc createIter*(): ptr TIter{.stdcall, importc: "bson_iterator_create", 
                               dynlib: bsondll.}
proc dispose*(a2: ptr TIter){.stdcall, importc: "bson_iterator_dispose", 
                              dynlib: bsondll.}

proc initIter*(b: var TBson): TIter =
  ## Initialize a bson iterator from the value `b`.
  proc iterator_init(i: var TIter, b: var TBson){.stdcall, 
      importc: "bson_iterator_init", dynlib: bsondll.}

  iterator_init(result, b)

proc fromBuffer*(i: var TIter, buffer: cstring) {.stdcall, 
    importc: "bson_iterator_from_buffer", dynlib: bsondll.}
  ## Initialize a bson iterator from a cstring buffer. Note
  ## that this is mostly used internally.

proc more*(i: var TIter): bool = 
  ## Check to see if the bson_iterator has more data.
  proc iterator_more(i: var TIter): TBsonBool{.stdcall, 
      importc: "bson_iterator_more", dynlib: bsondll.}
  result = iterator_more(i) != 0'i32
  
proc next*(i: var TIter): TBsonKind {.stdcall, 
    importc: "bson_iterator_next", dynlib: bsondll.}
  ## Point the iterator at the next BSON object.

proc kind*(i: var TIter): TBsonKind{.stdcall, 
    importc: "bson_iterator_type", dynlib: bsondll.}
  ## Get the type of the BSON object currently pointed to by the iterator.

proc key*(i: var TIter): cstring{.stdcall, 
    importc: "bson_iterator_key", dynlib: bsondll.}
  ##  Get the key of the BSON object currently pointed to by the iterator.
  
proc value*(i: var TIter): cstring{.stdcall, 
    importc: "bson_iterator_value", dynlib: bsondll.}
  ## Get the value of the BSON object currently pointed to by the iterator.
  
proc floatVal*(i: var TIter): float {.stdcall, 
    importc: "bson_iterator_double", dynlib: bsondll.}
  ## Get the double value of the BSON object currently pointed to by the
  ## iterator.

proc intVal*(i: var TIter): cint{.stdcall, importc: "bson_iterator_int", 
                                  dynlib: bsondll.}
  ## Get the int value of the BSON object currently pointed to by the iterator.

proc int64Val*(i: var TIter): int64{.stdcall, 
    importc: "bson_iterator_long", dynlib: bsondll.}
  ## Get the long value of the BSON object currently pointed to by the iterator.

proc timestamp*(i: var TIter): Ttimestamp {.stdcall, 
    importc: "bson_iterator_timestamp", dynlib: bsondll.}
  # return the bson timestamp as a whole or in parts 

proc timestampTime*(i: var TIter): cint {.stdcall, 
    importc: "bson_iterator_timestamp_time", dynlib: bsondll.}
  # return the bson timestamp as a whole or in parts 
proc timestampIncrement*(i: var TIter): cint{.stdcall, 
    importc: "bson_iterator_timestamp_increment", dynlib: bsondll.}
  # return the bson timestamp as a whole or in parts 

proc boolVal*(i: var TIter): TBsonBool{.stdcall, 
    importc: "bson_iterator_bool", dynlib: bsondll.}
  ## Get the boolean value of the BSON object currently pointed to by
  ## the iterator.
  ##
  ## | false: boolean false, 0 in any type, or null 
  ## | true: anything else (even empty strings and objects) 

proc floatRaw*(i: var TIter): cdouble{.stdcall, 
    importc: "bson_iterator_double_raw", dynlib: bsondll.}
  ## Get the double value of the BSON object currently pointed to by the
  ## iterator. Assumes the correct type is used.
      
proc intRaw*(i: var TIter): cint{.stdcall, 
    importc: "bson_iterator_int_raw", dynlib: bsondll.}
  ## Get the int value of the BSON object currently pointed to by the
  ## iterator. Assumes the correct type is used.
    
proc int64Raw*(i: var TIter): int64{.stdcall, 
    importc: "bson_iterator_long_raw", dynlib: bsondll.}
  ## Get the long value of the BSON object currently pointed to by the
  ## iterator. Assumes the correct type is used.

proc boolRaw*(i: var TIter): TBsonBool{.stdcall, 
    importc: "bson_iterator_bool_raw", dynlib: bsondll.}
  ## Get the bson_bool_t value of the BSON object currently pointed to by the
  ## iterator. Assumes the correct type is used.

proc oidVal*(i: var TIter): ptr TOid {.stdcall, 
    importc: "bson_iterator_oid", dynlib: bsondll.}
  ## Get the bson_oid_t value of the BSON object currently pointed to by the
  ## iterator.

proc strVal*(i: var TIter): cstring {.stdcall, 
    importc: "bson_iterator_string", dynlib: bsondll.}
  ## Get the string value of the BSON object currently pointed to by the
  ## iterator.

proc strLen*(i: var TIter): cint {.stdcall, 
    importc: "bson_iterator_string_len", dynlib: bsondll.}
  ## Get the string length of the BSON object currently pointed to by the
  ## iterator.

proc code*(i: var TIter): cstring {.stdcall, 
    importc: "bson_iterator_code", dynlib: bsondll.}
  ## Get the code value of the BSON object currently pointed to by the
  ## iterator. Works with bson_code, bson_codewscope, and BSON_STRING
  ## returns ``nil`` for everything else.
    
proc codeScope*(i: var TIter, scope: var TBson) {.stdcall, 
    importc: "bson_iterator_code_scope", dynlib: bsondll.}
  ## Calls bson_empty on scope if not a bson_codewscope
  
proc date*(i: var TIter): Tdate {.stdcall, 
    importc: "bson_iterator_date", dynlib: bsondll.}
  ## Get the date value of the BSON object currently pointed to by the
  ## iterator.

proc time*(i: var TIter): TTime {.stdcall, 
    importc: "bson_iterator_time_t", dynlib: bsondll.}
  ## Get the time value of the BSON object currently pointed to by the
  ## iterator.

proc binLen*(i: var TIter): cint {.stdcall, 
    importc: "bson_iterator_bin_len", dynlib: bsondll.}
  ## Get the length of the BSON binary object currently pointed to by the
  ## iterator.

proc binType*(i: var TIter): char {.stdcall, 
    importc: "bson_iterator_bin_type", dynlib: bsondll.}
  ## Get the type of the BSON binary object currently pointed to by the
  ## iterator.

proc binData*(i: var TIter): cstring {.stdcall, 
    importc: "bson_iterator_bin_data", dynlib: bsondll.}
  ## Get the value of the BSON binary object currently pointed to by the
  ## iterator.

proc regex*(i: var TIter): cstring {.stdcall, 
    importc: "bson_iterator_regex", dynlib: bsondll.}
  ## Get the value of the BSON regex object currently pointed to by the
  ## iterator.

proc regexOpts*(i: var TIter): cstring {.stdcall, 
    importc: "bson_iterator_regex_opts", dynlib: bsondll.}
  ## Get the options of the BSON regex object currently pointed to by the
  ## iterator.

proc subobject*(i: var TIter, sub: var TBson) {.stdcall, 
    importc: "bson_iterator_subobject", dynlib: bsondll.}
  ## Get the BSON subobject currently pointed to by the
  ## iterator.

proc subiterator*(i: var TIter, sub: var TIter) {.stdcall, 
    importc: "bson_iterator_subiterator", dynlib: bsondll.}
  ## Get a bson_iterator that on the BSON subobject.


# ----------------------------
#   BUILDING
# ----------------------------

proc init*(b: var TBson) {.stdcall, importc: "bson_init", dynlib: bsondll.}
  ## Initialize a new bson object. If not created
  ## with bson_new, you must initialize each new bson
  ## object using this function.
  ##
  ## When finished, you must pass the bson object to bson_destroy().

proc init*(b: var TBson, data: cstring): cint {.stdcall, 
    importc: "bson_init_data", dynlib: bsondll.}
  ## Initialize a BSON object, and point its data
  ## pointer to the provided `data`.
  ## Returns OK or ERROR.

proc initFinished*(b: var TBson, data: cstring): cint {.stdcall, 
    importc: "bson_init_finished_data", dynlib: bsondll.}

proc initSize*(b: var TBson, size: cint) {.stdcall, importc: "bson_init_size", 
    dynlib: bsondll.}
  ## Initialize a BSON object, and set its buffer to the given size.
  ## Returns OK or ERROR.

proc ensureSpace*(b: var TBson, bytesNeeded: cint): cint {.stdcall, 
    importc: "bson_ensure_space", dynlib: bsondll.}
  ## Grow a bson object. `bytesNeeded` is the additional number of bytes needed.

proc finish*(b: var TBson): cint{.stdcall, importc: "bson_finish", 
                                  dynlib: bsondll, discardable.}
  ## Finalize a bson object. Returns the standard error code.
  ## To deallocate memory, call destroy on the bson object.

proc destroy*(b: var TBson){.stdcall, importc: "bson_destroy", dynlib: bsondll.}
  ## Destroy a bson object.

proc empty*(obj: var TBson) {.stdcall, importc: "bson_empty", 
                              dynlib: bsondll.}
  ## Sets a pointer to a static empty BSON object.
  ## `obj` is the BSON object to initialize. 

proc copy*(outp, inp: var TBson): cint{.stdcall, importc: "bson_copy", 
    dynlib: bsondll.}
  ## Make a complete copy of the a BSON object.
  ## The source bson object must be in a finished
  ## state; otherwise, the copy will fail.

proc add*(b: var TBson, name: cstring, oid: TOid) =
  ## adds an OID to `b`.
  proc appendOid(b: var TBson, name: cstring, oid: ptr TOid): cint {.stdcall, 
      importc: "bson_append_oid", dynlib: bsondll.}
  
  var oid = oid
  discard appendOid(b, name, addr(oid))

proc add*(b: var TBson, name: cstring, i: cint): cint{.stdcall, 
    importc: "bson_append_int", dynlib: bsondll, discardable.}
  ## Append an int to a bson.

proc add*(b: var TBson, name: cstring, i: int64): cint{.stdcall, 
    importc: "bson_append_long", dynlib: bsondll, discardable.}
  ## Append an long to a bson.

proc add*(b: var TBson, name: cstring, d: float): cint{.stdcall, 
    importc: "bson_append_double", dynlib: bsondll, discardable.}
  ## Append an double to a bson.

proc add*(b: var TBson, name: cstring, str: cstring): cint {.stdcall, 
    importc: "bson_append_string", dynlib: bsondll, discardable.}
  ## Append a string to a bson.

proc add*(b: var TBson, name: cstring, str: cstring, len: cint): cint{.
    stdcall, importc: "bson_append_string_n", dynlib: bsondll, discardable.}
  ## Append len bytes of a string to a bson.

proc add*(b: var TBson, name: cstring, str: string) =
  ## Append a Nimrod string `str` to a bson.
  discard add(b, name, str, str.len.cint)

proc addSymbol*(b: var TBson, name: cstring, str: cstring): cint{.stdcall, 
    importc: "bson_append_symbol", dynlib: bsondll, discardable.}
  ##  Append a symbol to a bson.

proc addSymbol*(b: var TBson, name: cstring, str: cstring, len: cint): cint{.
    stdcall, importc: "bson_append_symbol_n", dynlib: bsondll, discardable.}
  ## Append len bytes of a symbol to a bson.

proc addCode*(b: var TBson, name: cstring, str: cstring): cint{.stdcall, 
    importc: "bson_append_code", dynlib: bsondll, discardable.}
  ## Append code to a bson.

proc addCode*(b: var TBson, name: cstring, str: cstring, len: cint): cint{.
    stdcall, importc: "bson_append_code_n", dynlib: bsondll, discardable.}
  ## Append len bytes of code to a bson.

proc addCode*(b: var TBson, name: cstring, code: cstring, 
                          scope: var TBson): cint{.stdcall, 
    importc: "bson_append_code_w_scope", dynlib: bsondll, discardable.}
  ## Append code to a bson with scope.

proc addCode*(b: var TBson, name: cstring, code: cstring, 
              size: cint, scope: var TBson): cint{.stdcall, 
    importc: "bson_append_code_w_scope_n", dynlib: bsondll, discardable.}
  ## Append len bytes of code to a bson with scope.

proc addBinary*(b: var TBson, name: cstring, typ: char, str: cstring, 
                len: cint): cint{.stdcall, importc: "bson_append_binary", 
                                 dynlib: bsondll, discardable.}
  ## Append binary data to a bson.

proc addBinary*(b: var TBson, name: cstring, data: string) =
  ## Append binary data to a bson.
  addBinary(b, name, '\5', data, data.len.cint)

proc addBool*(b: var TBson, name: cstring, v: TBsonBool): cint{.stdcall, 
    importc: "bson_append_bool", dynlib: bsondll, discardable.}
  ## Append a bson_bool_t to a bson.

proc addNull*(b: var TBson, name: cstring): cint {.stdcall, 
    importc: "bson_append_null", dynlib: bsondll, discardable.}
  ## Append a null value to a bson.

proc addUndefined*(b: var TBson, name: cstring): cint{.stdcall, 
    importc: "bson_append_undefined", dynlib: bsondll, discardable.}
  ## Append an undefined value to a bson.

proc addRegex*(b: var TBson, name: cstring, pattern: cstring, opts: cstring): cint{.
    stdcall, importc: "bson_append_regex", dynlib: bsondll, discardable.}
  ## Append a regex value to a bson.

proc add*(b: var TBson, name: cstring, TBson: var TBson): cint {.stdcall, 
    importc: "bson_append_bson", dynlib: bsondll, discardable.}
  ## Append bson data to a bson.

proc addElement*(b: var TBson, name_or_null: cstring, elem: var TIter): cint{.
    stdcall, importc: "bson_append_element", dynlib: bsondll, discardable.}
  ## Append a BSON element to a bson from the current point of an iterator.

proc addTimestamp*(b: var TBson, name: cstring, ts: var TTimestamp): cint{.
    stdcall, importc: "bson_append_timestamp", dynlib: bsondll, discardable.}
  ## Append a bson_timestamp_t value to a bson.

proc addTimestamp2*(b: var TBson, name: cstring, time: cint, increment: cint): cint{.
    stdcall, importc: "bson_append_timestamp2", dynlib: bsondll, discardable.}
proc addDate*(b: var TBson, name: cstring, millis: TDate): cint{.stdcall, 
    importc: "bson_append_date", dynlib: bsondll, discardable.}
  ## Append a bson_date_t value to a bson.

proc addTime*(b: var TBson, name: cstring, secs: TTime): cint{.stdcall, 
    importc: "bson_append_time_t", dynlib: bsondll, discardable.}
  ## Append a time_t value to a bson.

proc addStartObject*(b: var TBson, name: cstring): cint {.stdcall, 
    importc: "bson_append_start_object", dynlib: bsondll, discardable.}
  ## Start appending a new object to a bson.

proc addStartArray*(b: var TBson, name: cstring): cint {.stdcall, 
    importc: "bson_append_start_array", dynlib: bsondll, discardable.}
  ## Start appending a new array to a bson.

proc addFinishObject*(b: var TBson): cint {.stdcall, 
    importc: "bson_append_finish_object", dynlib: bsondll, discardable.}
  ## Finish appending a new object or array to a bson.

proc addFinishArray*(b: var TBson): cint {.stdcall, 
    importc: "bson_append_finish_array", dynlib: bsondll, discardable.}
  ## Finish appending a new object or array to a bson. This
  ## is simply an alias for bson_append_finish_object.

proc numstr*(str: cstring, i: cint){.stdcall, importc: "bson_numstr", 
                                     dynlib: bsondll.}
proc incnumstr*(str: cstring){.stdcall, importc: "bson_incnumstr", 
                               dynlib: bsondll.}

type 
  TErrHandler* = proc (errmsg: cstring){.
    stdcall.} ## an error handler. Error handlers shouldn't return!

proc setBsonErrHandler*(func: TErrHandler): TErrHandler {.stdcall, 
    importc: "set_bson_err_handler", dynlib: bsondll.}
  ## Set a function for error handling.
  ## Returns the old error handling function, or nil.

proc fatal*(ok: cint){.stdcall, importc: "bson_fatal", dynlib: bsondll.}
  ## does nothing if ok != 0. Exit fatally.

proc fatal*(ok: cint, msg: cstring){.stdcall, importc: "bson_fatal_msg", 
    dynlib: bsondll.}
  ## Exit fatally with an error message.

proc builderError*(b: var TBson){.stdcall, importc: "bson_builder_error", 
                                   dynlib: bsondll.}
  ## Invoke the error handler, but do not exit.

proc int64ToDouble*(i64: int64): cdouble {.stdcall, 
    importc: "bson_int64_to_double", dynlib: bsondll.}
  ## Cast an int64_t to double. This is necessary for embedding in
  ## certain environments.

const 
  MAJOR* = 0
  MINOR* = 4
  PATCH* = 0

type 
  TError*{.size: sizeof(cint).} = enum ## connection errors
    CONN_SUCCESS = 0,         ## Connection success! 
    CONN_NO_SOCKET,           ## Could not create a socket. 
    CONN_FAIL,                ## An error occured while calling connect(). 
    CONN_ADDR_FAIL,           ## An error occured while calling getaddrinfo(). 
    CONN_NOT_MASTER,          ## Warning: connected to a non-master node (read-only). 
    CONN_BAD_SET_NAME,        ## Given rs name doesn't match this replica set. 
    CONN_NO_PRIMARY,          ## Can't find primary in replica set. Connection closed. 
    IO_ERROR,                 ## An error occurred while reading or writing on the socket. 
    READ_SIZE_ERROR,          ## The response is not the expected length. 
    COMMAND_FAILED,           ## The command returned with 'ok' value of 0. 
    BSON_INVALID,             ## BSON not valid for the specified op. 
    BSON_NOT_FINISHED         ## BSON object has not been finished. 
  TCursorError*{.size: sizeof(cint).} = enum ## cursor error 
    CURSOR_EXHAUSTED,         ## The cursor has no more results. 
    CURSOR_INVALID,           ## The cursor has timed out or is not recognized. 
    CURSOR_PENDING,           ## Tailable cursor still alive but no data. 
    CURSOR_QUERY_FAIL,  ## The server returned an '$err' object, indicating query failure.
                        ## See conn.lasterrcode and conn.lasterrstr for details. 
    CURSOR_BSON_ERROR ## Something is wrong with the BSON provided. See conn.err
                      ## for details. 
  TCursorFlags* = enum ## cursor flags
    CURSOR_MUST_FREE = 1,     ## mongo_cursor_destroy should free cursor. 
    CURSOR_QUERY_SENT = (1 shl 1) ## Initial query has been sent. 
  TindexOpts* = enum 
    INDEX_UNIQUE = (1 shl 0), INDEX_DROP_DUPS = (1 shl 2), 
    INDEX_BACKGROUND = (1 shl 3), INDEX_SPARSE = (1 shl 4)
  TupdateOpts* = enum 
    UPDATE_UPSERT = 0x00000001, 
    UPDATE_MULTI = 0x00000002, 
    UPDATE_BASIC = 0x00000004
  TCursorOpts* = enum 
    TAILABLE = (1 shl 1),     ## Create a tailable cursor. 
    SLAVE_OK = (1 shl 2),     ## Allow queries on a non-primary node. 
    NO_CURSOR_TIMEOUT = (1 shl 4),  ## Disable cursor timeouts. 
    AWAIT_DATA = (1 shl 5),   ## Momentarily block for more data. 
    EXHAUST = (1 shl 6),      ## Stream in multiple 'more' packages. 
    PARTIAL = (1 shl 7)       ## Allow reads even if a shard is down. 
  Toperations* = enum 
    OP_MSG = 1000, OP_UPDATE = 2001, OP_INSERT = 2002, OP_QUERY = 2004, 
    OP_GET_MORE = 2005, OP_DELETE = 2006, OP_KILL_CURSORS = 2007
  THeader* {.pure, final.} = object 
    len*: cint
    id*: cint
    responseTo*: cint
    op*: cint

  TMessage* {.pure, final.} = object 
    head*: Theader
    data*: char

  TReplyFields*{.pure, final.} = object 
    flag*: cint               # FIX THIS COMMENT non-zero on failure 
    cursorID*: int64
    start*: cint
    num*: cint

  TReply*{.pure, final.} = object 
    head*: Theader
    fields*: Treply_fields
    objs*: char

  THostPort*{.pure, final.} = object 
    host*: array[0..255 - 1, char]
    port*: cint
    next*: ptr THostPort

  TReplset*{.pure, final.} = object ## replset
    seeds*: ptr THostPort    ## List of seeds provided by the user. 
    hosts*: ptr THostPort    ## List of host/ports given by the replica set 
    name*: cstring           ## Name of the replica set. 
    primary_connected*: TBsonBool ## Primary node connection status. 
  
  TMongo*{.pure, final.} = object ## mongo
    primary*: ptr THostPort   ## Primary connection info. 
    replset*: ptr TReplSet    ## replset object if connected to a replica set. 
    sock*: cint               ## Socket file descriptor. 
    flags*: cint              ## Flags on this connection object. 
    conn_timeout_ms*: cint    ## Connection timeout in milliseconds. 
    op_timeout_ms*: cint      ## Read and write timeout in milliseconds. 
    connected*: TBsonBool     ## Connection status. 
    err*: TError              ## Most recent driver error code. 
    errstr*: array[0..128 - 1, char] ## String version of most recent driver error code. 
    lasterrcode*: cint        ## getlasterror code given by the server on error. 
    lasterrstr*: cstring      ## getlasterror string generated by server. 
  
  TCursor*{.pure, final.} = object ## cursor
    reply*: ptr TReply        ## reply is owned by cursor 
    conn*: ptr TMongo         ## connection is *not* owned by cursor 
    ns*: cstring              ## owned by cursor 
    flags*: cint              ## Flags used internally by this drivers. 
    seen*: cint               ## Number returned so far. 
    current*: TBson           ## This cursor's current bson object. 
    err*: TCursorError        ## Errors on this cursor. 
    query*: ptr TBson         ## Bitfield containing cursor options. 
    fields*: ptr TBson        ## Bitfield containing cursor options. 
    options*: cint            ## Bitfield containing cursor options. 
    limit*: cint              ## Bitfield containing cursor options. 
    skip*: cint               ## Bitfield containing cursor options. 
  

# Connection API 

proc createMongo*(): ptr TMongo{.stdcall, importc: "mongo_create", dynlib: mongodll.}
proc dispose*(conn: ptr TMongo){.stdcall, importc: "mongo_dispose", 
                                 dynlib: mongodll.}
proc getErr*(conn: var TMongo): cint{.stdcall, importc: "mongo_get_err", 
                                     dynlib: mongodll.}
proc isConnected*(conn: var TMongo): cint{.stdcall, 
    importc: "mongo_is_connected", dynlib: mongodll.}
proc getOpTimeout*(conn: var TMongo): cint{.stdcall, 
    importc: "mongo_get_op_timeout", dynlib: mongodll.}
proc getPrimary*(conn: var TMongo): cstring{.stdcall, 
    importc: "mongo_get_primary", dynlib: mongodll.}
proc getSocket*(conn: var TMongo): cint {.stdcall, importc: "mongo_get_socket", 
    dynlib: mongodll.}
proc getHostCount*(conn: var TMongo): cint{.stdcall, 
    importc: "mongo_get_host_count", dynlib: mongodll.}
proc getHost*(conn: var TMongo, i: cint): cstring {.stdcall, 
    importc: "mongo_get_host", dynlib: mongodll.}
proc createCursor*(): ptr TCursor{.stdcall, importc: "mongo_cursor_create", 
                                  dynlib: mongodll.}
proc dispose*(cursor: ptr TCursor){.stdcall, 
    importc: "mongo_cursor_dispose", dynlib: mongodll.}
proc getServerErr*(conn: var TMongo): cint{.stdcall, 
    importc: "mongo_get_server_err", dynlib: mongodll.}
proc getServerErrString*(conn: var TMongo): cstring{.stdcall, 
    importc: "mongo_get_server_err_string", dynlib: mongodll.}

proc init*(conn: var TMongo){.stdcall, importc: "mongo_init", dynlib: mongodll.}
  ## Initialize a new mongo connection object. You must initialize each mongo
  ## object using this function.
  ## When finished, you must pass this object to ``destroy``.

proc connect*(conn: var TMongo, host: cstring = defaultHost, 
              port: cint = defaultPort): cint {.stdcall, 
    importc: "mongo_connect", dynlib: mongodll.}
  ## Connect to a single MongoDB server.

proc replsetInit*(conn: var TMongo, name: cstring){.stdcall, 
    importc: "mongo_replset_init", dynlib: mongodll.}
  ## Set up this connection object for connecting to a replica set.
  ## To connect, pass the object to replsetConnect.
  ## `name` is the name of the replica set to connect to.

proc replsetAddSeed*(conn: var TMongo, host: cstring = defaultHost, 
  port: cint = defaultPort){.stdcall,
  importc: "mongo_replset_add_seed", dynlib: mongodll.}
  ## Add a seed node to the replica set connection object.
  ## You must specify at least one seed node before connecting
  ## to a replica set.

proc parseHost*(hostString: cstring, hostPort: var ThostPort){.stdcall, 
    importc: "mongo_parse_host", dynlib: mongodll.}
  ## Utility function for converting a host-port string to a mongo_host_port.
  ## `hostString` is a string containing either a host or a host and port
  ## separated by a colon.
  ## `hostPort` is the mongo_host_port object to write the result to.

proc replsetConnect*(conn: var TMongo): cint{.stdcall, 
    importc: "mongo_replset_connect", dynlib: mongodll.}
  ## Connect to a replica set.
  ## Before passing a connection object to this function, you must already
  ## have called setReplset and replsetAddSeed.

proc setOpTimeout*(conn: var TMongo, millis: cint): cint{.stdcall, 
    importc: "mongo_set_op_timeout", dynlib: mongodll.}
  ## Set a timeout for operations on this connection. This
  ## is a platform-specific feature, and only work on Unix-like
  ## systems. You must also compile for linux to support this.

proc checkConnection*(conn: var TMongo): cint {.stdcall, 
    importc: "mongo_check_connection", dynlib: mongodll.}
  ## Ensure that this connection is healthy by performing
  ## a round-trip to the server.
  ## Returns OK if connected; otherwise ERROR.

proc reconnect*(conn: var TMongo): cint {.stdcall, importc: "mongo_reconnect", 
    dynlib: mongodll.}
  ## Try reconnecting to the server using the existing connection settings.
  ## This function will disconnect the current socket. If you've authenticated,
  ## you'll need to re-authenticate after calling this function.

proc disconnect*(conn: var TMongo){.stdcall, importc: "mongo_disconnect", 
                                    dynlib: mongodll.}
  ## Close the current connection to the server. After calling
  ## this function, you may call reconnect with the same
  ## connection object.

proc destroy*(conn: var TMongo){.stdcall, importc: "mongo_destroy", 
                                 dynlib: mongodll.}
  ## Close any existing connection to the server and free all allocated
  ## memory associated with the conn object.
  ## You must always call this function when finished with the connection
  ## object.

proc insert*(conn: var TMongo, ns: cstring, data: var TBson): cint{.stdcall, 
    importc: "mongo_insert", dynlib: mongodll, discardable.}
  ## Insert a BSON document into a MongoDB server. This function
  ## will fail if the supplied BSON struct is not UTF-8 or if
  ## the keys are invalid for insert (contain '.' or start with '$').

proc insertBatch*(conn: var TMongo, ns: cstring, 
                  data: ptr ptr TBson, num: cint): cint{.
    stdcall, importc: "mongo_insert_batch", dynlib: mongodll, discardable.}
  ## Insert a batch of BSON documents into a MongoDB server. This function
  ## will fail if any of the documents to be inserted is invalid.
  ## `num` is the number of documents in data.

proc update*(conn: var TMongo, ns: cstring, cond, op: var TBson, 
             flags: cint): cint{.stdcall, importc: "mongo_update", 
                                 dynlib: mongodll, discardable.}
  ## Update a document in a MongoDB server.
  ## 
  ## | conn a mongo object.
  ## | ns the namespace.
  ## | cond the bson update query.
  ## | op the bson update data.
  ## | flags flags for the update.
  ## | returns OK or ERROR with error stored in conn object.

proc remove*(conn: var TMongo, namespace: cstring, cond: var TBson): cint{.stdcall, 
    importc: "mongo_remove", dynlib: mongodll.}
  ## Remove a document from a MongoDB server.
  ##
  ## | conn a mongo object.
  ## | ns the namespace.
  ## | cond the bson query.
  ## | returns OK or ERROR with error stored in conn object.

proc find*(conn: var TMongo, namespace: cstring, query, fields: var TBson, 
           limit, skip: cint, options: cint): ptr TCursor{.stdcall, 
    importc: "mongo_find", dynlib: mongodll.}
  ## Find documents in a MongoDB server.
  ##
  ## | conn a mongo object.
  ## | ns the namespace.
  ## | query the bson query.
  ## | fields a bson document of fields to be returned.
  ## | limit the maximum number of documents to return.
  ## | skip the number of documents to skip.
  ## | options A bitfield containing cursor options.
  ## | returns A cursor object allocated on the heap or nil if
  ##   an error has occurred. For finer-grained error checking,
  ##   use the cursor builder API instead.

proc init*(cursor: var TCursor, conn: var TMongo, namespace: cstring){.stdcall, 
    importc: "mongo_cursor_init", dynlib: mongodll.}
  ## Initalize a new cursor object.
  ##
  ## The namespace is represented as the database
  ## name and collection name separated by a dot. e.g., "test.users".

proc setQuery*(cursor: var TCursor, query: var TBson) {.stdcall, 
    importc: "mongo_cursor_set_query", dynlib: mongodll.}
  ##  Set the bson object specifying this cursor's query spec. If
  ## your query is the empty bson object "{}", then you need not
  ## set this value.
  ##
  ## `query` is a bson object representing the query spec. This may
  ## be either a simple query spec or a complex spec storing values for
  ## $query, $orderby, $hint, and/or $explain. See
  ## http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol for details.

proc setFields*(cursor: var TCursor, fields: var TBson){.stdcall, 
    importc: "mongo_cursor_set_fields", dynlib: mongodll.}
  ## Set the fields to return for this cursor. If you want to return
  ## all fields, you need not set this value.
  ## `fields` is a bson object representing the fields to return.
  ## See http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields.

proc setSkip*(cursor: var TCursor, skip: cint){.stdcall, 
    importc: "mongo_cursor_set_skip", dynlib: mongodll.}
  ##  Set the number of documents to skip.

proc setLimit*(cursor: var TCursor, limit: cint){.stdcall, 
    importc: "mongo_cursor_set_limit", dynlib: mongodll.}
  ## Set the number of documents to return.

proc setOptions*(cursor: var TCursor, options: cint){.stdcall, 
    importc: "mongo_cursor_set_options", dynlib: mongodll.}
  ## Set any of the available query options (e.g., TAILABLE).
  ## See `TCursorOpts` for available constants.

proc data*(cursor: var TCursor): cstring {.stdcall, 
    importc: "mongo_cursor_data", dynlib: mongodll.}
  ## Return the current BSON object data as a ``cstring``. This is useful
  ## for creating bson iterators.

proc bson*(cursor: var TCursor): ptr TBson{.stdcall, 
    importc: "mongo_cursor_bson", dynlib: mongodll.}
  ## Return the current BSON object.

proc next*(cursor: var TCursor): cint {.stdcall, 
    importc: "mongo_cursor_next", dynlib: mongodll.}
  ## Iterate the cursor, returning the next item. When successful,
  ## the returned object will be stored in cursor.current;

proc destroy*(cursor: var TCursor): cint {.stdcall,
    importc: "mongo_cursor_destroy", dynlib: mongodll, discardable.}
  ## Destroy a cursor object. When finished with a cursor, you
  ## must pass it to this function.

proc findOne*(conn: var TMongo, namespace: cstring, query: var TBson, 
              fields: var TBson, outp: var TBson): cint{.stdcall, 
    importc: "mongo_find_one", dynlib: mongodll.}
  ## Find a single document in a MongoDB server.
  ##
  ## | conn a mongo object.
  ## | ns the namespace.
  ## | query the bson query.
  ## | fields a bson document of the fields to be returned.
  ## | outp a bson document in which to put the query result.
  ##   outp can be nil if you don't care about results. Useful for commands.

proc count*(conn: var TMongo, db: cstring, coll: cstring, query: var TBson): cdouble{.
    stdcall, importc: "mongo_count", dynlib: mongodll.}
  ## Count the number of documents in a collection matching a query.
  ##
  ## | conn a mongo object.
  ## | db the db name.
  ## | coll the collection name.
  ## | query the BSON query.
  ## | returns the number of matching documents. If the command fails,
  ##   ERROR is returned.

proc createIndex*(conn: var TMongo, namespace: cstring, key: var TBson, 
                   options: cint, outp: var TBson): cint {.stdcall, 
    importc: "mongo_create_index", dynlib: mongodll.}
  ##  Create a compouned index.
  ##
  ## | conn a mongo object.
  ## | ns the namespace.
  ## | data the bson index data.
  ## | options a bitfield for setting index options. Possibilities include
  ##   INDEX_UNIQUE, INDEX_DROP_DUPS, INDEX_BACKGROUND,
  ##   and INDEX_SPARSE.
  ## | out a bson document containing errors, if any.
  ## | returns MONGO_OK if index is created successfully; otherwise, MONGO_ERROR.

proc createSimpleIndex*(conn: var TMongo, namespace, field: cstring, 
                        options: cint, outp: var TBson): TBsonBool {.stdcall, 
    importc: "mongo_create_simple_index", dynlib: mongodll.}
  ## Create an index with a single key.
  ##
  ## | conn a mongo object.
  ## | ns the namespace.
  ## | field the index key.
  ## | options index options.
  ## | out a BSON document containing errors, if any.
  ## | returns true if the index was created.


# ----------------------------
#   COMMANDS
# ----------------------------


proc runCommand*(conn: var TMongo, db: cstring, command: var TBson, 
                  outp: var TBson): cint{.stdcall, importc: "mongo_run_command", 
    dynlib: mongodll.}
  ## Run a command on a MongoDB server.
  ## 
  ## | conn a mongo object.
  ## | db the name of the database.
  ## | command the BSON command to run.
  ## | out the BSON result of the command.
  ## | returns OK if the command ran without error.

proc simpleIntCommand*(conn: var TMongo, db: cstring, cmd: cstring, arg: cint, 
                         outp: var TBson): cint{.stdcall, 
    importc: "mongo_simple_int_command", dynlib: mongodll.}
  ## Run a command that accepts a simple string key and integer value.
  ##
  ## | conn a mongo object.
  ## | db the name of the database.
  ## | cmd the command to run.
  ## | arg the integer argument to the command.
  ## | out the BSON result of the command.
  ## | returns OK or an error code.

proc simpleStrCommand*(conn: var TMongo, db: cstring, cmd: cstring, 
                         arg: cstring, outp: var TBson): cint{.stdcall, 
    importc: "mongo_simple_str_command", dynlib: mongodll.}
  ## Run a command that accepts a simple string key and value.
  ##
  ## | conn a mongo object.
  ## | db the name of the database.
  ## | cmd the command to run.
  ## | arg the string argument to the command.
  ## | out the BSON result of the command.
  ## | returns true if the command ran without error.

proc cmdDropDb*(conn: var TMongo, db: cstring): cint{.stdcall, 
    importc: "mongo_cmd_drop_db", dynlib: mongodll.}
  ## Drop a database.
  ##
  ## | conn a mongo object.
  ## | db the name of the database to drop.
  ## | returns OK or an error code.

proc cmdDropCollection*(conn: var TMongo, db: cstring, collection: cstring, 
                          outp: var TBson): cint{.stdcall, 
    importc: "mongo_cmd_drop_collection", dynlib: mongodll.}
  ## Drop a collection.
  ##
  ## | conn a mongo object.
  ## | db the name of the database.
  ## | collection the name of the collection to drop.
  ## | out a BSON document containing the result of the command.
  ## | returns true if the collection drop was successful.

proc cmdAddUser*(conn: var TMongo, db: cstring, user: cstring, pass: cstring): cint{.
    stdcall, importc: "mongo_cmd_add_user", dynlib: mongodll.}
  ## Add a database user.
  ##
  ## | conn a mongo object.
  ## | db the database in which to add the user.
  ## | user the user name
  ## | pass the user password
  ## | returns OK or ERROR.

proc cmdAuthenticate*(conn: var TMongo, db: cstring, user: cstring, 
                      pass: cstring): cint{.stdcall, 
    importc: "mongo_cmd_authenticate", dynlib: mongodll.}
  ## Authenticate a user.
  ##
  ## | conn a mongo object.
  ## | db the database to authenticate against.
  ## | user the user name to authenticate.
  ## | pass the user's password.
  ## | returns OK on sucess and ERROR on failure.

proc cmdIsMaster*(conn: var TMongo, outp: var TBson): TBsonBool {.stdcall, 
    importc: "mongo_cmd_ismaster", dynlib: mongodll.}
  ## Check if the current server is a master.
  ##
  ## | conn a mongo object.
  ## | outp a BSON result of the command.
  ## | returns true if the server is a master.

proc cmdGetLastError*(conn: var TMongo, db: cstring, outp: var TBson): cint{.
    stdcall, importc: "mongo_cmd_get_last_error", dynlib: mongodll.}
  ## Get the error for the last command with the current connection.
  ##
  ## | conn a mongo object.
  ## | db the name of the database.
  ## | outp a BSON object containing the error details.
  ## | returns OK or ERROR

proc cmdGetPrevError*(conn: var TMongo, db: cstring, outp: var TBson): cint{.
    stdcall, importc: "mongo_cmd_get_prev_error", dynlib: mongodll.}
  ## Get the most recent error with the current connection.
  ##
  ## | conn a mongo object.
  ## | db the name of the database.
  ## | outp a BSON object containing the error details.
  ## | returns OK or ERROR.
  
proc cmdResetError*(conn: var TMongo, db: cstring){.stdcall, 
    importc: "mongo_cmd_reset_error", dynlib: mongodll.}
  ## Reset the error state for the connection. `db` is the name of the database.

# gridfs.h 

const 
  DEFAULT_CHUNK_SIZE* = 262144

type 
  TOffset* = int64

# A GridFS represents a single collection of GridFS files in the database. 

type 
  TGridfs*{.pure, final.} = object 
    client*: ptr TMongo       ## The client to db-connection. 
    dbname*: cstring          ## The root database name 
    prefix*: cstring          ## The prefix of the GridFS's collections,
                              ## default is nil 
    files_ns*: cstring        ## The namespace where the file's metadata
                              ## is stored
    chunks_ns*: cstring       ## The namespace where the files's data is
                              ## stored in chunks

# A GridFile is a single GridFS file. 

type 
  TGridFile*{.pure, final.} = object 
    gfs*: ptr TGridfs         ## GridFS where the GridFile is located 
    meta*: ptr TBson          ## GridFile's bson object where all
                              ## its metadata is located 
    pos*: TOffset             ## position is the offset in the file 
    id*: TOid                 ## files_id of the gridfile 
    remote_name*: cstring     ## name of the gridfile as a string 
    content_type*: cstring    ## gridfile's content type 
    length*: TOffset          ## length of this gridfile 
    chunk_num*: cint          ## number of the current chunk being written to 
    pending_data*: cstring    ## buffer storing data still to be
                              ## written to chunks 
    pending_len*: cint        ## length of pending_data buffer 
  

proc createGridfs*(): ptr TGridfs{.stdcall, importc: "gridfs_create", dynlib: mongodll.}
proc dispose*(gfs: ptr TGridfs){.stdcall, importc: "gridfs_dispose", 
                                 dynlib: mongodll.}
proc createGridfile*(): ptr TGridFile{.stdcall, importc: "gridfile_create", 
                               dynlib: mongodll.}
proc dispose*(gf: ptr TGridFile){.stdcall, importc: "gridfile_dispose", 
                                  dynlib: mongodll.}
proc getDescriptor*(gf: var TGridFile, outp: var TBson){.stdcall, 
    importc: "gridfile_get_descriptor", dynlib: mongodll.}


proc init*(client: var TMongo, dbname: cstring, prefix: cstring, 
           gfs: var TGridfs): cint{.stdcall, importc: "gridfs_init", 
                                    dynlib: mongodll.}
  ## Initializes a GridFS object
  ## 
  ## | client - db connection
  ## | dbname - database name
  ## | prefix - collection prefix, default is fs if NULL or empty
  ## | gfs - the GridFS object to initialize
  ## | returns - OK or ERROR.

proc destroy*(gfs: var TGridfs){.stdcall, importc: "gridfs_destroy", 
                                 dynlib: mongodll.}
  ## Destroys a GridFS object. Call this when finished with the object.

proc writerInit*(gfile: var TGridFile, gfs: var TGridfs, remote_name: cstring, 
                  content_type: cstring){.stdcall, 
    importc: "gridfile_writer_init", dynlib: mongodll.}
  ## Initializes a gridfile for writing incrementally with ``writeBuffer``.
  ## Once initialized, you can write any number of buffers with ``writeBuffer``.
  ## When done, you must call ``writerDone`` to save the file metadata.

proc writeBuffer*(gfile: var TGridFile, data: cstring, length: TOffset){.
    stdcall, importc: "gridfile_write_buffer", dynlib: mongodll.}
  ## Write to a GridFS file incrementally. You can call this function any number
  ## of times with a new buffer each time. This allows you to effectively
  ## stream to a GridFS file. When finished, be sure to call ``writerDone``.

proc writerDone*(gfile: var TGridFile): cint{.stdcall, 
    importc: "gridfile_writer_done", dynlib: mongodll.}
  ## Signal that writing of this gridfile is complete by
  ## writing any buffered chunks along with the entry in the
  ## files collection. Returns OK or ERROR.

proc storeBuffer*(gfs: var TGridfs, data: cstring, length: TOffset, 
                   remotename: cstring, contenttype: cstring): cint{.stdcall, 
    importc: "gridfs_store_buffer", dynlib: mongodll.}
  ## Store a buffer as a GridFS file.
  ##
  ## | gfs - the working GridFS
  ## | data - pointer to buffer to store in GridFS
  ## | length - length of the buffer
  ## | remotename - filename for use in the database
  ## | contenttype - optional MIME type for this object
  ## | returns - MONGO_OK or MONGO_ERROR.

proc storeFile*(gfs: var TGridfs, filename: cstring, remotename: cstring, 
                 contenttype: cstring): cint{.stdcall, 
    importc: "gridfs_store_file", dynlib: mongodll.}
  ## Open the file referenced by filename and store it as a GridFS file.
  ## 
  ## | gfs - the working GridFS
  ## | filename - local filename relative to the process
  ## | remotename - optional filename for use in the database
  ## | contenttype - optional MIME type for this object
  ## | returns - OK or ERROR.

proc removeFilename*(gfs: var TGridfs, filename: cstring){.stdcall, 
    importc: "gridfs_remove_filename", dynlib: mongodll.}
  ## Removes the files referenced by filename from the db.

proc findQuery*(gfs: var TGridfs, query: var TBson, gfile: var TGridFile): cint{.
    stdcall, importc: "gridfs_find_query", dynlib: mongodll.}
  ## Find the first file matching the provided query within the
  ## GridFS files collection, and return the file as a GridFile.
  ## Returns OK if successful, ERROR otherwise.
  
proc findFilename*(gfs: var TGridfs, filename: cstring, gfile: var TGridFile): cint{.
    stdcall, importc: "gridfs_find_filename", dynlib: mongodll.}
  ## Find the first file referenced by filename within the GridFS
  ## and return it as a GridFile. Returns OK or ERROR.

proc init*(gfs: var TGridfs, meta: var TBson, gfile: var TGridFile): cint{.
    stdcall, importc: "gridfile_init", dynlib: mongodll.}
  ## Initializes a GridFile containing the GridFS and file bson.

proc destroy*(gfile: var TGridFile){.stdcall, importc: "gridfile_destroy", 
                                     dynlib: mongodll.}
  ## Destroys the GridFile.

proc exists*(gfile: var TGridFile): TBsonBool{.stdcall, 
    importc: "gridfile_exists", dynlib: mongodll.}
  ## Returns whether or not the GridFile exists.

proc getFilename*(gfile: var TGridFile): cstring{.stdcall, 
    importc: "gridfile_get_filename", dynlib: mongodll.}
  ## Returns the filename of GridFile.

proc getChunksize*(gfile: var TGridFile): cint{.stdcall, 
    importc: "gridfile_get_chunksize", dynlib: mongodll.}
  ## Returns the size of the chunks of the GridFile.

proc getContentlength*(gfile: var TGridFile): TOffset{.stdcall, 
    importc: "gridfile_get_contentlength", dynlib: mongodll.}
  ## Returns the length of GridFile's data.

proc getContenttype*(gfile: var TGridFile): cstring{.stdcall, 
    importc: "gridfile_get_contenttype", dynlib: mongodll.}
  ## Returns the MIME type of the GridFile (nil if no type specified).

proc getUploaddate*(gfile: var TGridFile): Tdate{.stdcall, 
    importc: "gridfile_get_uploaddate", dynlib: mongodll.}
  ## Returns the upload date of GridFile.

proc getMd5*(gfile: var TGridFile): cstring {.stdcall, 
    importc: "gridfile_get_md5", dynlib: mongodll.}
  ## Returns the MD5 of GridFile.

proc getField*(gfile: var TGridFile, name: cstring): cstring{.stdcall, 
    importc: "gridfile_get_field", dynlib: mongodll.}
  ## Returns the field in GridFile specified by name. Returns the data of the
  ## field specified (nil if none exists).

proc getBoolean*(gfile: var TGridFile, name: cstring): TBsonBool{.stdcall, 
    importc: "gridfile_get_boolean", dynlib: mongodll.}
  ## Returns a boolean field in GridFile specified by name.

proc getMetadata*(gfile: var TGridFile, outp: var TBson){.stdcall, 
    importc: "gridfile_get_metadata", dynlib: mongodll.}
  ## Returns the metadata of GridFile (an empty bson is returned if none
  ## exists).

proc getNumchunks*(gfile: var TGridFile): cint{.stdcall, 
    importc: "gridfile_get_numchunks", dynlib: mongodll.}
  ## Returns the number of chunks in the GridFile.

proc getChunk*(gfile: var TGridFile, n: cint, outp: var TBson){.stdcall, 
    importc: "gridfile_get_chunk", dynlib: mongodll.}
  ## Returns chunk `n` of GridFile.

proc getChunks*(gfile: var TGridFile, start: cint, size: cint): ptr TCursor{.
    stdcall, importc: "gridfile_get_chunks", dynlib: mongodll.}
  ## Returns a mongo_cursor of `size` chunks starting with chunk `start`.
  ## The cursor must be destroyed after use.

proc writeFile*(gfile: ptr TGridFile, stream: TFile): TOffset{.stdcall, 
    importc: "gridfile_write_file", dynlib: mongodll.}
  ## Writes the GridFile to a stream.

proc read*(gfile: var TGridFile, size: TOffset, buf: cstring): TOffset{.stdcall, 
    importc: "gridfile_read", dynlib: mongodll.}
  ## Reads length bytes from the GridFile to a buffer
  ## and updates the position in the file.
  ## (assumes the buffer is large enough)
  ## (if size is greater than EOF gridfile_read reads until EOF).
  ## Returns the number of bytes read.

proc seek*(gfile: var TGridFile, offset: TOffset): TOffset{.stdcall, 
    importc: "gridfile_seek", dynlib: mongodll.}
  ## Updates the position in the file
  ## (If the offset goes beyond the contentlength,
  ## the position is updated to the end of the file.)
  ## Returns the offset location
