/**
 * @file mongo.h
 * @brief Main MongoDB Declarations
 */

/*    Copyright 2009-2012 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.
 */

#ifndef MONGO_H_
#define MONGO_H_

#include "bson.h"

MONGO_EXTERN_C_START

#define MONGO_MAJOR 0
#define MONGO_MINOR 7
#define MONGO_PATCH 0

#define MONGO_OK 0
#define MONGO_ERROR -1

#define MONGO_DEFAULT_PORT 27017

#define MONGO_DEFAULT_MAX_BSON_SIZE 4 * 1024 * 1024

#define MONGO_ERR_LEN 128

typedef enum mongo_error_t {
    MONGO_CONN_SUCCESS = 0,  /**< Connection success! */
    MONGO_CONN_NO_SOCKET,    /**< Could not create a socket. */
    MONGO_CONN_FAIL,         /**< An error occured while calling connect(). */
    MONGO_CONN_ADDR_FAIL,    /**< An error occured while calling getaddrinfo(). */
    MONGO_CONN_NOT_MASTER,   /**< Warning: connected to a non-master node (read-only). */
    MONGO_CONN_BAD_SET_NAME, /**< Given rs name doesn't match this replica set. */
    MONGO_CONN_NO_PRIMARY,   /**< Can't find primary in replica set. Connection closed. */

    MONGO_IO_ERROR,          /**< An error occurred while reading or writing on the socket. */
    MONGO_SOCKET_ERROR,      /**< Other socket error. */
    MONGO_READ_SIZE_ERROR,   /**< The response is not the expected length. */
    MONGO_COMMAND_FAILED,    /**< The command returned with 'ok' value of 0. */
    MONGO_WRITE_ERROR,       /**< Write with given write_concern returned an error. */
    MONGO_NS_INVALID,        /**< The name for the ns (database or collection) is invalid. */
    MONGO_BSON_INVALID,      /**< BSON not valid for the specified op. */
    MONGO_BSON_NOT_FINISHED, /**< BSON object has not been finished. */
    MONGO_BSON_TOO_LARGE,    /**< BSON object exceeds max BSON size. */
    MONGO_WRITE_CONCERN_INVALID /**< Supplied write concern object is invalid. */
} mongo_error_t;

typedef enum mongo_cursor_error_t {
    MONGO_CURSOR_EXHAUSTED,  /**< The cursor has no more results. */
    MONGO_CURSOR_INVALID,    /**< The cursor has timed out or is not recognized. */
    MONGO_CURSOR_PENDING,    /**< Tailable cursor still alive but no data. */
    MONGO_CURSOR_QUERY_FAIL, /**< The server returned an '$err' object, indicating query failure.
                                  See conn->lasterrcode and conn->lasterrstr for details. */
    MONGO_CURSOR_BSON_ERROR  /**< Something is wrong with the BSON provided. See conn->err
                                  for details. */
} mongo_cursor_error_t;

enum mongo_cursor_flags {
    MONGO_CURSOR_MUST_FREE = 1,      /**< mongo_cursor_destroy should free cursor. */
    MONGO_CURSOR_QUERY_SENT = ( 1<<1 ) /**< Initial query has been sent. */
};

enum mongo_index_opts {
    MONGO_INDEX_UNIQUE = ( 1<<0 ),
    MONGO_INDEX_DROP_DUPS = ( 1<<2 ),
    MONGO_INDEX_BACKGROUND = ( 1<<3 ),
    MONGO_INDEX_SPARSE = ( 1<<4 )
};

enum mongo_update_opts {
    MONGO_UPDATE_UPSERT = 0x1,
    MONGO_UPDATE_MULTI = 0x2,
    MONGO_UPDATE_BASIC = 0x4
};

enum mongo_insert_opts {
    MONGO_CONTINUE_ON_ERROR = 0x1
};

enum mongo_cursor_opts {
    MONGO_TAILABLE = ( 1<<1 ),        /**< Create a tailable cursor. */
    MONGO_SLAVE_OK = ( 1<<2 ),        /**< Allow queries on a non-primary node. */
    MONGO_NO_CURSOR_TIMEOUT = ( 1<<4 ), /**< Disable cursor timeouts. */
    MONGO_AWAIT_DATA = ( 1<<5 ),      /**< Momentarily block for more data. */
    MONGO_EXHAUST = ( 1<<6 ),         /**< Stream in multiple 'more' packages. */
    MONGO_PARTIAL = ( 1<<7 )          /**< Allow reads even if a shard is down. */
};

enum mongo_operations {
    MONGO_OP_MSG = 1000,
    MONGO_OP_UPDATE = 2001,
    MONGO_OP_INSERT = 2002,
    MONGO_OP_QUERY = 2004,
    MONGO_OP_GET_MORE = 2005,
    MONGO_OP_DELETE = 2006,
    MONGO_OP_KILL_CURSORS = 2007
};

#pragma pack(1)
typedef struct {
    int len;
    int id;
    int responseTo;
    int op;
} mongo_header;

typedef struct {
    mongo_header head;
    char data;
} mongo_message;

typedef struct {
    int flag; /* FIX THIS COMMENT non-zero on failure */
    int64_t cursorID;
    int start;
    int num;
} mongo_reply_fields;

typedef struct {
    mongo_header head;
    mongo_reply_fields fields;
    char objs;
} mongo_reply;
#pragma pack()

typedef struct mongo_host_port {
    char host[255];
    int port;
    struct mongo_host_port *next;
} mongo_host_port;

typedef struct mongo_write_concern {
    int w;            /**< Number of total replica write copies to complete including the primary. */
    int wtimeout;     /**< Number of milliseconds before replication timeout. */
    int j;            /**< If non-zero, block until the journal sync. */
    int fsync;        /**< Same a j with journaling enabled; otherwise, call fsync. */
    const char *mode; /**< Either "majority" or a getlasterrormode. Overrides w value. */

    bson *cmd; /**< The BSON object representing the getlasterror command. */
} mongo_write_concern;

typedef struct {
    mongo_host_port *seeds;        /**< List of seeds provided by the user. */
    mongo_host_port *hosts;        /**< List of host/ports given by the replica set */
    char *name;                    /**< Name of the replica set. */
    bson_bool_t primary_connected; /**< Primary node connection status. */
} mongo_replica_set;

typedef struct mongo {
    mongo_host_port *primary;  /**< Primary connection info. */
    mongo_replica_set *replica_set;    /**< replica_set object if connected to a replica set. */
    int sock;                  /**< Socket file descriptor. */
    int flags;                 /**< Flags on this connection object. */
    int conn_timeout_ms;       /**< Connection timeout in milliseconds. */
    int op_timeout_ms;         /**< Read and write timeout in milliseconds. */
    int max_bson_size;         /**< Largest BSON object allowed on this connection. */
    bson_bool_t connected;     /**< Connection status. */
    mongo_write_concern *write_concern; /**< The default write concern. */

    mongo_error_t err;          /**< Most recent driver error code. */
    int errcode;                /**< Most recent errno or WSAGetLastError(). */
    char errstr[MONGO_ERR_LEN]; /**< String version of error. */
    int lasterrcode;            /**< getlasterror code from the server. */
    char lasterrstr[MONGO_ERR_LEN]; /**< getlasterror string from the server. */
} mongo;

typedef struct {
    mongo_reply *reply;  /**< reply is owned by cursor */
    mongo *conn;       /**< connection is *not* owned by cursor */
    const char *ns;    /**< owned by cursor */
    int flags;         /**< Flags used internally by this drivers. */
    int seen;          /**< Number returned so far. */
    bson current;      /**< This cursor's current bson object. */
    mongo_cursor_error_t err; /**< Errors on this cursor. */
    const bson *query; /**< Bitfield containing cursor options. */
    const bson *fields;/**< Bitfield containing cursor options. */
    int options;       /**< Bitfield containing cursor options. */
    int limit;         /**< Bitfield containing cursor options. */
    int skip;          /**< Bitfield containing cursor options. */
} mongo_cursor;

/*********************************************************************
Connection API
**********************************************************************/

/** Initialize sockets for Windows.
 */
MONGO_EXPORT void mongo_init_sockets( void );

/**
 * Initialize a new mongo connection object. You must initialize each mongo
 * object using this function.
 *
 *  @note When finished, you must pass this object to
 *      mongo_destroy( ).
 *
 *  @param conn a mongo connection object allocated on the stack
 *      or heap.
 */
MONGO_EXPORT void mongo_init( mongo *conn );

/**
 * Connect to a single MongoDB server.
 *
 * @param conn a mongo object.
 * @param host a numerical network address or a network hostname.
 * @param port the port to connect to.
 *
 * @return MONGO_OK or MONGO_ERROR on failure. On failure, a constant of type
 *   mongo_error_t will be set on the conn->err field.
 */
MONGO_EXPORT int mongo_client( mongo *conn , const char *host, int port );

/**
 * DEPRECATED - use mongo_client.
 * Connect to a single MongoDB server.
 *
 * @param conn a mongo object.
 * @param host a numerical network address or a network hostname.
 * @param port the port to connect to.
 *
 * @return MONGO_OK or MONGO_ERROR on failure. On failure, a constant of type
 *   mongo_error_t will be set on the conn->err field.
 */
MONGO_EXPORT int mongo_connect( mongo *conn , const char *host, int port );

/**
 * Set up this connection object for connecting to a replica set.
 * To connect, pass the object to mongo_replica_set_connect().
 *
 * @param conn a mongo object.
 * @param name the name of the replica set to connect to.
 * */
MONGO_EXPORT void mongo_replica_set_init( mongo *conn, const char *name );

/**
 * DEPRECATED - use mongo_replica_set_init.
 * Set up this connection object for connecting to a replica set.
 * To connect, pass the object to mongo_replset_connect().
 *
 * @param conn a mongo object.
 * @param name the name of the replica set to connect to.
 * */
MONGO_EXPORT void mongo_replset_init( mongo *conn, const char *name );

/**
 * Add a seed node to the replica set connection object.
 *
 * You must specify at least one seed node before connecting to a replica set.
 *
 * @param conn a mongo object.
 * @param host a numerical network address or a network hostname.
 * @param port the port to connect to.
 */
MONGO_EXPORT void mongo_replica_set_add_seed( mongo *conn, const char *host, int port );

/**
 * DEPRECATED - use mongo_replica_set_add_seed.
 * Add a seed node to the replica set connection object.
 *
 * You must specify at least one seed node before connecting to a replica set.
 *
 * @param conn a mongo object.
 * @param host a numerical network address or a network hostname.
 * @param port the port to connect to.
 */
MONGO_EXPORT void mongo_replset_add_seed( mongo *conn, const char *host, int port );

/**
 * Utility function for converting a host-port string to a mongo_host_port.
 *
 * @param host_string a string containing either a host or a host and port separated
 *     by a colon.
 * @param host_port the mongo_host_port object to write the result to.
 */
void mongo_parse_host( const char *host_string, mongo_host_port *host_port );

/**
 * Utility function for validation database and collection names.
 *
 * @param conn a mongo object.
 *
 * @return MONGO_OK or MONGO_ERROR on failure. On failure, a constant of type
 *   mongo_conn_return_t will be set on the conn->err field.
 *
 */
MONGO_EXPORT int mongo_validate_ns( mongo *conn, const char *ns );

/**
 * Connect to a replica set.
 *
 * Before passing a connection object to this function, you must already have called
 * mongo_set_replica_set and mongo_replica_set_add_seed.
 *
 * @param conn a mongo object.
 *
 * @return MONGO_OK or MONGO_ERROR on failure. On failure, a constant of type
 *   mongo_conn_return_t will be set on the conn->err field.
 */
MONGO_EXPORT int mongo_replica_set_client( mongo *conn );

/**
 * DEPRECATED - use mongo_replica_set_client.
 * Connect to a replica set.
 *
 * Before passing a connection object to this function, you must already have called
 * mongo_set_replset and mongo_replset_add_seed.
 *
 * @param conn a mongo object.
 *
 * @return MONGO_OK or MONGO_ERROR on failure. On failure, a constant of type
 *   mongo_conn_return_t will be set on the conn->err field.
 */
MONGO_EXPORT int mongo_replset_connect( mongo *conn );

/** Set a timeout for operations on this connection. This
 *  is a platform-specific feature, and only work on *nix
 *  system. You must also compile for linux to support this.
 *
 *  @param conn a mongo object.
 *  @param millis timeout time in milliseconds.
 *
 *  @return MONGO_OK. On error, return MONGO_ERROR and
 *    set the conn->err field.
 */
MONGO_EXPORT int mongo_set_op_timeout( mongo *conn, int millis );

/**
 * Ensure that this connection is healthy by performing
 * a round-trip to the server.
 *
 * @param conn a mongo connection
 *
 * @return MONGO_OK if connected; otherwise, MONGO_ERROR.
 */
MONGO_EXPORT int mongo_check_connection( mongo *conn );

/**
 * 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.
 *
 * @param conn a mongo object.
 *
 * @return MONGO_OK or MONGO_ERROR and
 *   set the conn->err field.
 */
MONGO_EXPORT int mongo_reconnect( mongo *conn );

/**
 * Close the current connection to the server. After calling
 * this function, you may call mongo_reconnect with the same
 * connection object.
 *
 * @param conn a mongo object.
 */
MONGO_EXPORT void mongo_disconnect( mongo *conn );

/**
 * 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.
 *
 * @param conn a mongo object.
 */
MONGO_EXPORT void mongo_destroy( mongo *conn );

/**
 * Specify the write concern object that this connection should use
 * by default for all writes (inserts, updates, and deletes). This value
 * can be overridden by passing a write_concern object to any write function.
 *
 * @param conn a mongo object.
 * @param write_concern pointer to a write concern object.
 *
 */
MONGO_EXPORT void mongo_set_write_concern( mongo *conn,
        mongo_write_concern *write_concern );


/*********************************************************************
CRUD API
**********************************************************************/

/**
 * 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 '$').
 *
 * The default write concern set on the conn object will be used.
 *
 * @param conn a mongo object.
 * @param ns the namespace.
 * @param data the bson data.
 * @param custom_write_concern a write concern object that will
 *     override any write concern set on the conn object.
 *
 * @return MONGO_OK or MONGO_ERROR. If the conn->err
 *     field is MONGO_BSON_INVALID, check the err field
 *     on the bson struct for the reason.
 */
MONGO_EXPORT int mongo_insert( mongo *conn, const char *ns, const bson *data,
                               mongo_write_concern *custom_write_concern );

/**
 * Insert a batch of BSON documents into a MongoDB server. This function
 * will fail if any of the documents to be inserted is invalid.
 *
 * The default write concern set on the conn object will be used.
 *
 * @param conn a mongo object.
 * @param ns the namespace.
 * @param data the bson data.
 * @param num the number of documents in data.
 * @param custom_write_concern a write concern object that will
 *     override any write concern set on the conn object.
 * @param flags flags on this batch insert. Currently, this value
 *     may be 0 or MONGO_CONTINUE_ON_ERROR, which will cause the
 *     batch insert to continue even if a given insert in the batch fails.
 *
 * @return MONGO_OK or MONGO_ERROR.
 *
 */
MONGO_EXPORT int mongo_insert_batch( mongo *conn, const char *ns,
                                     const bson **data, int num, mongo_write_concern *custom_write_concern,
                                     int flags );

/**
 * Update a document in a MongoDB server.
 *
 * The default write concern set on the conn object will be used.
 *
 * @param conn a mongo object.
 * @param ns the namespace.
 * @param cond the bson update query.
 * @param op the bson update data.
 * @param flags flags for the update.
 * @param custom_write_concern a write concern object that will
 *     override any write concern set on the conn object.
 *
 * @return MONGO_OK or MONGO_ERROR with error stored in conn object.
 *
 */
MONGO_EXPORT int mongo_update( mongo *conn, const char *ns, const bson *cond,
                               const bson *op, int flags, mongo_write_concern *custom_write_concern );

/**
 * Remove a document from a MongoDB server.
 *
 * The default write concern set on the conn object will be used.
 *
 * @param conn a mongo object.
 * @param ns the namespace.
 * @param cond the bson query.
 * @param custom_write_concern a write concern object that will
 *     override any write concern set on the conn object.
 *
 * @return MONGO_OK or MONGO_ERROR with error stored in conn object.
 */
MONGO_EXPORT int mongo_remove( mongo *conn, const char *ns, const bson *cond,
                               mongo_write_concern *custom_write_concern );


/*********************************************************************
Write Concern API
**********************************************************************/

/**
 * Initialize a mongo_write_concern object. Effectively zeroes out the struct.
 *
 */
MONGO_EXPORT void mongo_write_concern_init( mongo_write_concern *write_concern );

/**
 * Finish this write concern object by serializing the literal getlasterror
 * command that will be sent to the server.
 *
 * You must call mongo_write_concern_destroy() to free the serialized BSON.
 *
 */
MONGO_EXPORT int mongo_write_concern_finish( mongo_write_concern *write_concern );

/**
 * Free the write_concern object (specifically, the BSON that it owns).
 *
 */
MONGO_EXPORT void mongo_write_concern_destroy( mongo_write_concern *write_concern );

/*********************************************************************
Cursor API
**********************************************************************/

/**
 * Find documents in a MongoDB server.
 *
 * @param conn a mongo object.
 * @param ns the namespace.
 * @param query the bson query.
 * @param fields a bson document of fields to be returned.
 * @param limit the maximum number of documents to retrun.
 * @param skip the number of documents to skip.
 * @param options A bitfield containing cursor options.
 *
 * @return A cursor object allocated on the heap or NULL if
 *     an error has occurred. For finer-grained error checking,
 *     use the cursor builder API instead.
 */
MONGO_EXPORT mongo_cursor *mongo_find( mongo *conn, const char *ns, const bson *query,
                                       const bson *fields, int limit, int skip, int options );

/**
 * Initalize a new cursor object.
 *
 * @param cursor
 * @param ns the namespace, represented as the the database
 *     name and collection name separated by a dot. e.g., "test.users"
 */
MONGO_EXPORT void mongo_cursor_init( mongo_cursor *cursor, mongo *conn, const char *ns );

/**
 * 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.
 *
 * @param cursor
 * @param query 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.
 */
MONGO_EXPORT void mongo_cursor_set_query( mongo_cursor *cursor, const bson *query );

/**
 * Set the fields to return for this cursor. If you want to return
 * all fields, you need not set this value.
 *
 * @param cursor
 * @param fields a bson object representing the fields to return.
 *   See http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields.
 */
MONGO_EXPORT void mongo_cursor_set_fields( mongo_cursor *cursor, const bson *fields );

/**
 * Set the number of documents to skip.
 *
 * @param cursor
 * @param skip
 */
MONGO_EXPORT void mongo_cursor_set_skip( mongo_cursor *cursor, int skip );

/**
 * Set the number of documents to return.
 *
 * @param cursor
 * @param limit
 */
MONGO_EXPORT void mongo_cursor_set_limit( mongo_cursor *cursor, int limit );

/**
 * Set any of the available query options (e.g., MONGO_TAILABLE).
 *
 * @param cursor
 * @param options a bitfield storing query options. See
 *   mongo_cursor_bitfield_t for available constants.
 */
MONGO_EXPORT void mongo_cursor_set_options( mongo_cursor *cursor, int options );

/**
 * Return the current BSON object data as a const char*. This is useful
 * for creating bson iterators with bson_iterator_init.
 *
 * @param cursor
 */
MONGO_EXPORT const char *mongo_cursor_data( mongo_cursor *cursor );

/**
 * Return the current BSON object data as a const char*. This is useful
 * for creating bson iterators with bson_iterator_init.
 *
 * @param cursor
 */
MONGO_EXPORT const bson *mongo_cursor_bson( mongo_cursor *cursor );

/**
 * Iterate the cursor, returning the next item. When successful,
 *   the returned object will be stored in cursor->current;
 *
 * @param cursor
 *
 * @return MONGO_OK. On error, returns MONGO_ERROR and sets
 *   cursor->err with a value of mongo_error_t.
 */
MONGO_EXPORT int mongo_cursor_next( mongo_cursor *cursor );

/**
 * Destroy a cursor object. When finished with a cursor, you
 * must pass it to this function.
 *
 * @param cursor the cursor to destroy.
 *
 * @return MONGO_OK or an error code. On error, check cursor->conn->err
 *     for errors.
 */
MONGO_EXPORT int mongo_cursor_destroy( mongo_cursor *cursor );

/**
 * Find a single document in a MongoDB server.
 *
 * @param conn a mongo object.
 * @param ns the namespace.
 * @param query the bson query.
 * @param fields a bson document of the fields to be returned.
 * @param out a bson document in which to put the query result.
 *
 */
/* out can be NULL if you don't care about results. useful for commands */
MONGO_EXPORT int mongo_find_one( mongo *conn, const char *ns, const bson *query,
                                 const bson *fields, bson *out );


/*********************************************************************
Command API and Helpers
**********************************************************************/

/**
 * Count the number of documents in a collection matching a query.
 *
 * @param conn a mongo object.
 * @param db the db name.
 * @param coll the collection name.
 * @param query the BSON query.
 *
 * @return the number of matching documents. If the command fails,
 *     MONGO_ERROR is returned.
 */
MONGO_EXPORT double mongo_count( mongo *conn, const char *db, const char *coll,
                                 const bson *query );

/**
 * Create a compound index.
 *
 * @param conn a mongo object.
 * @param ns the namespace.
 * @param data the bson index data.
 * @param options a bitfield for setting index options. Possibilities include
 *   MONGO_INDEX_UNIQUE, MONGO_INDEX_DROP_DUPS, MONGO_INDEX_BACKGROUND,
 *   and MONGO_INDEX_SPARSE.
 * @param out a bson document containing errors, if any.
 *
 * @return MONGO_OK if index is created successfully; otherwise, MONGO_ERROR.
 */
MONGO_EXPORT int mongo_create_index( mongo *conn, const char *ns,
                                     const bson *key, int options, bson *out );

/**
 * Create a capped collection.
 *
 * @param conn a mongo object.
 * @param ns the namespace (e.g., "dbname.collectioname")
 * @param size the size of the capped collection in bytes.
 * @param max the max number of documents this collection is
 *   allowed to contain. If zero, this argument will be ignored
 *   and the server will use the collection's size to age document out.
 *   If using this option, ensure that the total size can contain this
 *   number of documents.
 */
MONGO_EXPORT int mongo_create_capped_collection( mongo *conn, const char *db,
        const char *collection, int size, int max, bson *out );

/**
 * Create an index with a single key.
 *
 * @param conn a mongo object.
 * @param ns the namespace.
 * @param field the index key.
 * @param options index options.
 * @param out a BSON document containing errors, if any.
 *
 * @return true if the index was created.
 */
MONGO_EXPORT bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns,
        const char *field, int options, bson *out );

/**
 * Run a command on a MongoDB server.
 *
 * @param conn a mongo object.
 * @param db the name of the database.
 * @param command the BSON command to run.
 * @param out the BSON result of the command.
 *
 * @return MONGO_OK if the command ran without error.
 */
MONGO_EXPORT int mongo_run_command( mongo *conn, const char *db,
                                    const bson *command, bson *out );

/**
 * Run a command that accepts a simple string key and integer value.
 *
 * @param conn a mongo object.
 * @param db the name of the database.
 * @param cmd the command to run.
 * @param arg the integer argument to the command.
 * @param out the BSON result of the command.
 *
 * @return MONGO_OK or an error code.
 *
 */
MONGO_EXPORT int mongo_simple_int_command( mongo *conn, const char *db,
        const char *cmd, int arg, bson *out );

/**
 * Run a command that accepts a simple string key and value.
 *
 * @param conn a mongo object.
 * @param db the name of the database.
 * @param cmd the command to run.
 * @param arg the string argument to the command.
 * @param out the BSON result of the command.
 *
 * @return true if the command ran without error.
 *
 */
MONGO_EXPORT int mongo_simple_str_command( mongo *conn, const char *db,
        const char *cmd, const char *arg, bson *out );

/**
 * Drop a database.
 *
 * @param conn a mongo object.
 * @param db the name of the database to drop.
 *
 * @return MONGO_OK or an error code.
 */
MONGO_EXPORT int mongo_cmd_drop_db( mongo *conn, const char *db );

/**
 * Drop a collection.
 *
 * @param conn a mongo object.
 * @param db the name of the database.
 * @param collection the name of the collection to drop.
 * @param out a BSON document containing the result of the command.
 *
 * @return true if the collection drop was successful.
 */
MONGO_EXPORT int mongo_cmd_drop_collection( mongo *conn, const char *db,
        const char *collection, bson *out );

/**
 * Add a database user.
 *
 * @param conn a mongo object.
 * @param db the database in which to add the user.
 * @param user the user name
 * @param pass the user password
 *
 * @return MONGO_OK or MONGO_ERROR.
  */
MONGO_EXPORT int mongo_cmd_add_user( mongo *conn, const char *db,
                                     const char *user, const char *pass );

/**
 * Authenticate a user.
 *
 * @param conn a mongo object.
 * @param db the database to authenticate against.
 * @param user the user name to authenticate.
 * @param pass the user's password.
 *
 * @return MONGO_OK on sucess and MONGO_ERROR on failure.
 */
MONGO_EXPORT int mongo_cmd_authenticate( mongo *conn, const char *db,
        const char *user, const char *pass );

/**
 * Check if the current server is a master.
 *
 * @param conn a mongo object.
 * @param out a BSON result of the command.
 *
 * @return true if the server is a master.
 */
/* return value is master status */
MONGO_EXPORT bson_bool_t mongo_cmd_ismaster( mongo *conn, bson *out );

/**
 * Get the error for the last command with the current connection.
 *
 * @param conn a mongo object.
 * @param db the name of the database.
 * @param out a BSON object containing the error details.
 *
 * @return MONGO_OK if no error and MONGO_ERROR on error. On error, check the values
 *     of conn->lasterrcode and conn->lasterrstr for the error status.
 */
MONGO_EXPORT int mongo_cmd_get_last_error( mongo *conn, const char *db, bson *out );

/**
 * Get the most recent error with the current connection.
 *
 * @param conn a mongo object.
 * @param db the name of the database.
 * @param out a BSON object containing the error details.
 *
 * @return MONGO_OK if no error and MONGO_ERROR on error. On error, check the values
 *     of conn->lasterrcode and conn->lasterrstr for the error status.
 */
MONGO_EXPORT int mongo_cmd_get_prev_error( mongo *conn, const char *db, bson *out );

/**
 * Reset the error state for the connection.
 *
 * @param conn a mongo object.
 * @param db the name of the database.
 */
MONGO_EXPORT void mongo_cmd_reset_error( mongo *conn, const char *db );


/*********************************************************************
Utility API
**********************************************************************/

MONGO_EXPORT mongo* mongo_create( void );
MONGO_EXPORT void mongo_dispose(mongo* conn);
MONGO_EXPORT int mongo_get_err(mongo* conn);
MONGO_EXPORT int mongo_is_connected(mongo* conn);
MONGO_EXPORT int mongo_get_op_timeout(mongo* conn);
MONGO_EXPORT const char* mongo_get_primary(mongo* conn);
MONGO_EXPORT int mongo_get_socket(mongo* conn) ;
MONGO_EXPORT int mongo_get_host_count(mongo* conn);
MONGO_EXPORT const char* mongo_get_host(mongo* conn, int i);
MONGO_EXPORT mongo_cursor* mongo_cursor_create( void );
MONGO_EXPORT void mongo_cursor_dispose(mongo_cursor* cursor);
MONGO_EXPORT int  mongo_get_server_err(mongo* conn);
MONGO_EXPORT const char*  mongo_get_server_err_string(mongo* conn);

/**
 * Set an error on a mongo connection object. Mostly for internal use.
 *
 * @param conn a mongo connection object.
 * @param err a driver error code of mongo_error_t.
 * @param errstr a string version of the error.
 * @param errorcode Currently errno or WSAGetLastError().
 */
MONGO_EXPORT void __mongo_set_error( mongo *conn, mongo_error_t err,
                                     const char *errstr, int errorcode );
/**
 * Clear all errors stored on a mongo connection object.
 *
 * @param conn a mongo connection object.
 */
MONGO_EXPORT void mongo_clear_errors( mongo *conn );

MONGO_EXTERN_C_END

#endif
