/*
 * Copyright 2009-present MongoDB, 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.
 */

#include <mongoc/mongoc-prelude.h>

#ifndef MONGOC_CURSOR_PRIVATE_H
#define MONGOC_CURSOR_PRIVATE_H

#include <mongoc/mongoc-cursor.h> // IWYU pragma: export

//

#include <mongoc/mongoc-buffer-private.h>
#include <mongoc/mongoc-cluster-private.h>
#include <mongoc/mongoc-rpc-private.h>
#include <mongoc/mongoc-server-stream-private.h>

#include <mongoc/mongoc-client.h>

#include <bson/bson.h>


BSON_BEGIN_DECLS

#define MONGOC_CURSOR_ALLOW_PARTIAL_RESULTS "allowPartialResults"
#define MONGOC_CURSOR_ALLOW_PARTIAL_RESULTS_LEN 19
#define MONGOC_CURSOR_AWAIT_DATA "awaitData"
#define MONGOC_CURSOR_AWAIT_DATA_LEN 9
#define MONGOC_CURSOR_BATCH_SIZE "batchSize"
#define MONGOC_CURSOR_BATCH_SIZE_LEN 9
#define MONGOC_CURSOR_COLLATION "collation"
#define MONGOC_CURSOR_COLLATION_LEN 9
#define MONGOC_CURSOR_COMMENT "comment"
#define MONGOC_CURSOR_COMMENT_LEN 7
#define MONGOC_CURSOR_EXHAUST "exhaust"
#define MONGOC_CURSOR_EXHAUST_LEN 7
#define MONGOC_CURSOR_FILTER "filter"
#define MONGOC_CURSOR_FILTER_LEN 6
#define MONGOC_CURSOR_FIND "find"
#define MONGOC_CURSOR_FIND_LEN 4
#define MONGOC_CURSOR_HINT "hint"
#define MONGOC_CURSOR_HINT_LEN 4
#define MONGOC_CURSOR_LIMIT "limit"
#define MONGOC_CURSOR_LIMIT_LEN 5
#define MONGOC_CURSOR_MAX "max"
#define MONGOC_CURSOR_MAX_LEN 3
#define MONGOC_CURSOR_MAX_AWAIT_TIME_MS "maxAwaitTimeMS"
#define MONGOC_CURSOR_MAX_AWAIT_TIME_MS_LEN 14
#define MONGOC_CURSOR_MAX_SCAN "maxScan"
#define MONGOC_CURSOR_MAX_SCAN_LEN 7
#define MONGOC_CURSOR_MAX_TIME_MS "maxTimeMS"
#define MONGOC_CURSOR_MAX_TIME_MS_LEN 9
#define MONGOC_CURSOR_MIN "min"
#define MONGOC_CURSOR_MIN_LEN 3
#define MONGOC_CURSOR_NO_CURSOR_TIMEOUT "noCursorTimeout"
#define MONGOC_CURSOR_NO_CURSOR_TIMEOUT_LEN 15
#define MONGOC_CURSOR_OPLOG_REPLAY "oplogReplay"
#define MONGOC_CURSOR_OPLOG_REPLAY_LEN 11
#define MONGOC_CURSOR_ORDERBY "orderby"
#define MONGOC_CURSOR_ORDERBY_LEN 7
#define MONGOC_CURSOR_PROJECTION "projection"
#define MONGOC_CURSOR_PROJECTION_LEN 10
#define MONGOC_CURSOR_QUERY "query"
#define MONGOC_CURSOR_QUERY_LEN 5
#define MONGOC_CURSOR_READ_CONCERN "readConcern"
#define MONGOC_CURSOR_READ_CONCERN_LEN 11
#define MONGOC_CURSOR_RETURN_KEY "returnKey"
#define MONGOC_CURSOR_RETURN_KEY_LEN 9
#define MONGOC_CURSOR_SHOW_DISK_LOC "showDiskLoc"
#define MONGOC_CURSOR_SHOW_DISK_LOC_LEN 11
#define MONGOC_CURSOR_SHOW_RECORD_ID "showRecordId"
#define MONGOC_CURSOR_SHOW_RECORD_ID_LEN 12
#define MONGOC_CURSOR_SINGLE_BATCH "singleBatch"
#define MONGOC_CURSOR_SINGLE_BATCH_LEN 11
#define MONGOC_CURSOR_SKIP "skip"
#define MONGOC_CURSOR_SKIP_LEN 4
#define MONGOC_CURSOR_SNAPSHOT "snapshot"
#define MONGOC_CURSOR_SNAPSHOT_LEN 8
#define MONGOC_CURSOR_SORT "sort"
#define MONGOC_CURSOR_SORT_LEN 4
#define MONGOC_CURSOR_TAILABLE "tailable"
#define MONGOC_CURSOR_TAILABLE_LEN 8

typedef struct _mongoc_cursor_impl_t mongoc_cursor_impl_t;
typedef enum { UNPRIMED, IN_BATCH, END_OF_BATCH, DONE } mongoc_cursor_state_t;
typedef mongoc_cursor_state_t (*_mongoc_cursor_impl_transition_t)(mongoc_cursor_t *cursor);
struct _mongoc_cursor_impl_t {
   void (*clone)(mongoc_cursor_impl_t *dst, const mongoc_cursor_impl_t *src);
   void (*destroy)(mongoc_cursor_impl_t *ctx);
   _mongoc_cursor_impl_transition_t prime;
   _mongoc_cursor_impl_transition_t pop_from_batch;
   _mongoc_cursor_impl_transition_t get_next_batch;
   void *data;
};

/* 3.2+ responses -- read batch docs like {cursor:{id: 123, firstBatch: []}} */
typedef struct _mongoc_cursor_response_t {
   bson_t reply;           /* the entire command reply */
   bson_iter_t batch_iter; /* iterates over the batch array */
   bson_t current_doc;     /* the current doc inside the batch array */
} mongoc_cursor_response_t;

struct _mongoc_cursor_t {
   mongoc_client_t *client;
   uint32_t client_generation;

   uint32_t server_id;
   bool secondary_ok;

   mongoc_cursor_state_t state;
   bool in_exhaust;

   bson_t opts;

   mongoc_read_concern_t *read_concern;
   mongoc_read_prefs_t *read_prefs;
   mongoc_write_concern_t *write_concern;

   /** If the cursor was created for an operation that might have overridden the
    * user's read preferences' read mode, then server selection forced the
    * cursor to use a read preference mode of 'primary' server. Whether this
    * force occurred is stored here: */
   bool must_use_primary;

   /** Whether this cursor corresponds to an aggregate command that contains a
    * writing-stage */
   bool is_aggr_with_write_stage;

   bool explicit_session;
   mongoc_client_session_t *client_session;

   uint32_t count;

   char *ns;
   uint32_t nslen;
   uint32_t dblen;

   bson_error_t error;
   bson_t error_doc;        // Always initialized, and set with server errors.
   bool had_stream_timeout; // True if previous command run resulted in a timeout on the mongoc_stream_t.

   const bson_t *current;

   mongoc_cursor_impl_t impl;

   int64_t operation_id;
   int64_t cursor_id;
};

int32_t
_mongoc_n_return(mongoc_cursor_t *cursor);
void
_mongoc_set_cursor_ns(mongoc_cursor_t *cursor, const char *ns, uint32_t nslen);
bool
_mongoc_cursor_get_opt_bool(const mongoc_cursor_t *cursor, const char *option);
void
_mongoc_cursor_flags_to_opts(mongoc_query_flags_t qflags, bson_t *opts, bool *secondary_ok);
bool
_mongoc_cursor_translate_dollar_query_opts(const bson_t *query, bson_t *opts, bson_t *unwrapped, bson_error_t *error);
mongoc_server_stream_t *
_mongoc_cursor_fetch_stream(mongoc_cursor_t *cursor, const mongoc_ss_log_context_t *log_context);
void
_mongoc_cursor_collection(const mongoc_cursor_t *cursor, const char **collection, int *collection_len);
bool
_mongoc_cursor_run_command(mongoc_cursor_t *cursor, const bson_t *command, const bson_t *opts, bson_t *reply);
bool
_mongoc_cursor_more(mongoc_cursor_t *cursor);

bool
_mongoc_cursor_set_opt_int64(mongoc_cursor_t *cursor, const char *option, int64_t value);
void
_mongoc_cursor_prepare_find_command(mongoc_cursor_t *cursor, const bson_t *filter, bson_t *command);
const bson_t *
_mongoc_cursor_initial_query(mongoc_cursor_t *cursor);
const bson_t *
_mongoc_cursor_get_more(mongoc_cursor_t *cursor);
/* start iterating a reply like
 * {cursor: {id: 1234, ns: "db.collection", firstBatch: [...]}} or
 * {cursor: {id: 1234, ns: "db.collection", nextBatch: [...]}} */
void
_mongoc_cursor_response_refresh(mongoc_cursor_t *cursor,
                                const bson_t *command,
                                const bson_t *opts,
                                mongoc_cursor_response_t *response);
bool
_mongoc_cursor_start_reading_response(mongoc_cursor_t *cursor, mongoc_cursor_response_t *response);
void
_mongoc_cursor_response_read(mongoc_cursor_t *cursor, mongoc_cursor_response_t *response, const bson_t **bson);
void
_mongoc_cursor_prepare_getmore_command(mongoc_cursor_t *cursor, bson_t *command);
void
_mongoc_cursor_set_empty(mongoc_cursor_t *cursor);
bool
_mongoc_cursor_check_and_copy_to(mongoc_cursor_t *cursor, const char *err_prefix, const bson_t *src, bson_t *dst);
void
_mongoc_cursor_prime(mongoc_cursor_t *cursor);
bool
_mongoc_cursor_next(mongoc_cursor_t *cursor, const bson_t **bson);
mongoc_cursor_t *
_mongoc_cursor_new_with_opts(mongoc_client_t *client,
                             const char *db_and_collection,
                             const bson_t *opts,
                             const mongoc_read_prefs_t *user_prefs,
                             const mongoc_read_prefs_t *default_prefs,
                             const mongoc_read_concern_t *read_concern);
/* cursor constructors. */
mongoc_cursor_t *
_mongoc_cursor_find_new(mongoc_client_t *client,
                        const char *db_and_coll,
                        const bson_t *filter,
                        const bson_t *opts,
                        const mongoc_read_prefs_t *user_prefs,
                        const mongoc_read_prefs_t *default_prefs,
                        const mongoc_read_concern_t *read_concern);

mongoc_cursor_t *
_mongoc_cursor_cmd_new(mongoc_client_t *client,
                       const char *db_and_coll,
                       const bson_t *cmd,
                       const bson_t *opts,
                       const mongoc_read_prefs_t *user_prefs,
                       const mongoc_read_prefs_t *default_prefs,
                       const mongoc_read_concern_t *read_concern);

mongoc_cursor_t *
_mongoc_cursor_cmd_new_from_reply(mongoc_client_t *client, const bson_t *cmd, const bson_t *opts, bson_t *reply);

mongoc_cursor_t *
_mongoc_cursor_cmd_deprecated_new(mongoc_client_t *client,
                                  const char *db_and_coll,
                                  const bson_t *cmd,
                                  const mongoc_read_prefs_t *read_prefs);

mongoc_cursor_t *
_mongoc_cursor_array_new(
   mongoc_client_t *client, const char *db_and_coll, const bson_t *cmd, const bson_t *opts, const char *field_name);

mongoc_cursor_t *
_mongoc_cursor_change_stream_new(mongoc_client_t *client, bson_t *reply, const bson_t *opts);

bool
_mongoc_cursor_change_stream_end_of_batch(mongoc_cursor_t *cursor);

const bson_t *
_mongoc_cursor_change_stream_get_post_batch_resume_token(mongoc_cursor_t *cursor);

bool
_mongoc_cursor_change_stream_has_post_batch_resume_token(mongoc_cursor_t *cursor);

const bson_t *
_mongoc_cursor_change_stream_get_reply(mongoc_cursor_t *cursor);

BSON_END_DECLS


#endif /* MONGOC_CURSOR_PRIVATE_H */
