#ifndef _MODULE_H_
#define _MODULE_H_

/* This header file exposes a set of functions defined in module.c that are
 * not part of the module API, but are used by the core to interact with modules
 */

/* Extract encver / signature from a module type ID. */
#define VALKEYMODULE_TYPE_ENCVER_BITS 10
#define VALKEYMODULE_TYPE_ENCVER_MASK ((1 << VALKEYMODULE_TYPE_ENCVER_BITS) - 1)
#define VALKEYMODULE_TYPE_ENCVER(id) ((id) & VALKEYMODULE_TYPE_ENCVER_MASK)
#define VALKEYMODULE_TYPE_SIGN(id) \
    (((id) & ~((uint64_t)VALKEYMODULE_TYPE_ENCVER_MASK)) >> VALKEYMODULE_TYPE_ENCVER_BITS)

/* Bit flags for moduleTypeAuxSaveFunc */
#define VALKEYMODULE_AUX_BEFORE_RDB (1 << 0)
#define VALKEYMODULE_AUX_AFTER_RDB (1 << 1)

struct ValkeyModule;
struct ValkeyModuleIO;
struct ValkeyModuleDigest;
struct ValkeyModuleCtx;
struct moduleLoadQueueEntry;
struct ValkeyModuleKeyOptCtx;
struct ValkeyModuleCommand;
struct ValkeyModuleCommandResult;
struct clusterState;

/* Each module type implementation should export a set of methods in order
 * to serialize and deserialize the value in the RDB file, rewrite the AOF
 * log, create the digest for "DEBUG DIGEST", and free the value when a key
 * is deleted. */
typedef void *(*moduleTypeLoadFunc)(struct ValkeyModuleIO *io, int encver);
typedef void (*moduleTypeSaveFunc)(struct ValkeyModuleIO *io, void *value);
typedef int (*moduleTypeAuxLoadFunc)(struct ValkeyModuleIO *rdb, int encver, int when);
typedef void (*moduleTypeAuxSaveFunc)(struct ValkeyModuleIO *rdb, int when);
typedef void (*moduleTypeRewriteFunc)(struct ValkeyModuleIO *io, struct serverObject *key, void *value);
typedef void (*moduleTypeDigestFunc)(struct ValkeyModuleDigest *digest, void *value);
typedef size_t (*moduleTypeMemUsageFunc)(const void *value);
typedef void (*moduleTypeFreeFunc)(void *value);
typedef size_t (*moduleTypeFreeEffortFunc)(struct serverObject *key, const void *value);
typedef void (*moduleTypeUnlinkFunc)(struct serverObject *key, void *value);
typedef void *(*moduleTypeCopyFunc)(struct serverObject *fromkey, struct serverObject *tokey, const void *value);
typedef int (*moduleTypeDefragFunc)(struct ValkeyModuleDefragCtx *ctx, struct serverObject *key, void **value);
typedef size_t (*moduleTypeMemUsageFunc2)(struct ValkeyModuleKeyOptCtx *ctx, const void *value, size_t sample_size);
typedef void (*moduleTypeFreeFunc2)(struct ValkeyModuleKeyOptCtx *ctx, void *value);
typedef size_t (*moduleTypeFreeEffortFunc2)(struct ValkeyModuleKeyOptCtx *ctx, const void *value);
typedef void (*moduleTypeUnlinkFunc2)(struct ValkeyModuleKeyOptCtx *ctx, void *value);
typedef void *(*moduleTypeCopyFunc2)(struct ValkeyModuleKeyOptCtx *ctx, const void *value);
typedef int (*moduleTypeAuthCallback)(struct ValkeyModuleCtx *ctx, void *username, void *password, const char **err);


/* The module type, which is referenced in each value of a given type, defines
 * the methods and links to the module exporting the type. */
typedef struct ValkeyModuleType {
    uint64_t id; /* Higher 54 bits of type ID + 10 lower bits of encoding ver. */
    struct ValkeyModule *module;
    moduleTypeLoadFunc rdb_load;
    moduleTypeSaveFunc rdb_save;
    moduleTypeRewriteFunc aof_rewrite;
    moduleTypeMemUsageFunc mem_usage;
    moduleTypeDigestFunc digest;
    moduleTypeFreeFunc free;
    moduleTypeFreeEffortFunc free_effort;
    moduleTypeUnlinkFunc unlink;
    moduleTypeCopyFunc copy;
    moduleTypeDefragFunc defrag;
    moduleTypeAuxLoadFunc aux_load;
    moduleTypeAuxSaveFunc aux_save;
    moduleTypeMemUsageFunc2 mem_usage2;
    moduleTypeFreeEffortFunc2 free_effort2;
    moduleTypeUnlinkFunc2 unlink2;
    moduleTypeCopyFunc2 copy2;
    moduleTypeAuxSaveFunc aux_save2;
    int aux_save_triggers;
    char name[10]; /* 9 bytes name + null term. Charset: A-Z a-z 0-9 _- */
} moduleType;

/* In Object 'robj' structures of type OBJ_MODULE, the value pointer
 * is set to the following structure, referencing the moduleType structure
 * in order to work with the value, and at the same time providing a raw
 * pointer to the value, as created by the module commands operating with
 * the module type.
 *
 * So for example in order to free such a value, it is possible to use
 * the following code:
 *
 *  if (robj->type == OBJ_MODULE) {
 *      moduleValue *mt = robj->ptr;
 *      mt->type->free(mt->value);
 *      zfree(mt); // We need to release this in-the-middle struct as well.
 *  }
 */
typedef struct moduleValue {
    moduleType *type;
    void *value;
} moduleValue;

typedef int (*ModuleLoadFunc)(void *, void **, int);
typedef int (*ModuleUnLoadFunc)(void *);

/* This structure represents a module inside the system. */
typedef struct ValkeyModule {
    void *handle;                         /* Module dlopen() handle. */
    char *name;                           /* Module name. */
    int ver;                              /* Module version. We use just progressive integers. */
    int apiver;                           /* Module API version as requested during initialization.*/
    list *types;                          /* Module data types. */
    list *usedby;                         /* List of modules using APIs from this one. */
    list *using;                          /* List of modules we use some APIs of. */
    list *filters;                        /* List of filters the module has registered. */
    list *module_configs;                 /* List of configurations the module has registered */
    int configs_initialized;              /* Have the module configurations been initialized? */
    int in_call;                          /* RM_Call() nesting level */
    int in_hook;                          /* Hooks callback nesting level for this module (0 or 1). */
    int options;                          /* Module options and capabilities. */
    int blocked_clients;                  /* Count of ValkeyModuleBlockedClient in this module. */
    ValkeyModuleInfoFunc info_cb;         /* Callback for module to add INFO fields. */
    ValkeyModuleDefragFunc defrag_cb;     /* Callback for global data defrag. */
    struct moduleLoadQueueEntry *loadmod; /* Module load arguments for config rewrite. */
    int num_commands_with_acl_categories; /* Number of commands in this module included in acl categories */
    int onload;                           /* Flag to identify if the call is being made from Onload (0 or 1) */
    size_t num_acl_categories_added;      /* Number of acl categories added by this module. */
    int is_static_module;                 /* 1 if this is a static module, 0 otherwise */
} ValkeyModule;

/* This is a wrapper for the 'rio' streams used inside rdb.c in the server, so that
 * the user does not have to take the total count of the written bytes nor
 * to care about error conditions. */
typedef struct ValkeyModuleIO {
    size_t bytes;         /* Bytes read / written so far. */
    rio *rio;             /* Rio stream. */
    moduleType *type;     /* Module type doing the operation. */
    int error;            /* True if error condition happened. */
    ValkeyModuleCtx *ctx; /* Optional context, see RM_GetContextFromIO()*/
    robj *key;            /* Optional name of key processed */
    int dbid;             /* The dbid of the key being processed, -1 when unknown. */
    sds pre_flush_buffer; /* A buffer that should be flushed before next write operation
                           * See rdbSaveSingleModuleAux for more details */
} ValkeyModuleIO;

/* Macro to initialize an IO context. Note that the 'ver' field is populated
 * inside rdb.c according to the version of the value to load. */
static inline void moduleInitIOContext(ValkeyModuleIO *iovar,
                                       moduleType *mtype,
                                       rio *rioptr,
                                       robj *keyptr,
                                       int db) {
    iovar->rio = rioptr;
    iovar->type = mtype;
    iovar->bytes = 0;
    iovar->error = 0;
    iovar->key = keyptr;
    iovar->dbid = db;
    iovar->ctx = NULL;
    iovar->pre_flush_buffer = NULL;
}

/* This is a structure used to export DEBUG DIGEST capabilities to
 * modules. We want to capture both the ordered and unordered elements of
 * a data structure, so that a digest can be created in a way that correctly
 * reflects the values. See the DEBUG DIGEST command implementation for more
 * background. */
typedef struct ValkeyModuleDigest {
    unsigned char o[20]; /* Ordered elements. */
    unsigned char x[20]; /* Xored elements. */
    robj *key;           /* Optional name of key processed */
    int dbid;            /* The dbid of the key being processed */
} ValkeyModuleDigest;

/* Just start with a digest composed of all zero bytes. */
static inline void moduleInitDigestContext(ValkeyModuleDigest *mdvar) {
    memset(mdvar->o, 0, sizeof(mdvar->o));
    memset(mdvar->x, 0, sizeof(mdvar->x));
}

void moduleEnqueueLoadModule(sds path, sds *argv, int argc);
sds moduleLoadQueueEntryToLoadmoduleOptionStr(ValkeyModule *module,
                                              const char *config_option_str);
ValkeyModuleCtx *moduleAllocateContext(void);
void moduleScriptingEngineInitContext(ValkeyModuleCtx *out_ctx,
                                      ValkeyModule *module,
                                      int add_script_execution_flag,
                                      client *client);
void moduleFreeContext(ValkeyModuleCtx *ctx);
void moduleInitModulesSystem(void);
void moduleInitModulesSystemLast(void);
void modulesCron(void);
int moduleLoad(const char *path, void **argv, int argc, int is_loadex);
int moduleLoadStatic(const char *path, void **argv, int argc, int is_loadex);
int moduleUnload(sds name, const char **errmsg);
void moduleUnloadAllModules(void);
void moduleLoadFromQueue(void);
int moduleGetCommandKeysViaAPI(struct serverCommand *cmd, robj **argv, int argc, getKeysResult *result);
int moduleGetCommandChannelsViaAPI(struct serverCommand *cmd, robj **argv, int argc, getKeysResult *result);
moduleType *moduleTypeLookupModuleByID(uint64_t id);
moduleType *moduleTypeLookupModuleByName(const char *name);
moduleType *moduleTypeLookupModuleByNameIgnoreCase(const char *name);
void moduleTypeNameByID(char *name, uint64_t moduleid);
const char *moduleTypeModuleName(moduleType *mt);
const char *moduleNameFromCommand(struct serverCommand *cmd);
ValkeyModule *moduleFromCommand(struct serverCommand *cmd);
void moduleCallCommandUnblockedHandler(client *c);
int isModuleClientUnblocked(client *c);
void unblockClientFromModule(client *c);
void moduleHandleBlockedClients(void);
void moduleBlockedClientTimedOut(client *c, int from_module);
void modulePipeReadable(aeEventLoop *el, int fd, void *privdata, int mask);
size_t moduleCount(void);
void moduleAcquireGIL(void);
int moduleTryAcquireGIL(void);
void moduleReleaseGIL(void);
void moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid);
unsigned long moduleNotifyKeyspaceSubscribersCnt(void);
void firePostExecutionUnitJobs(void);
void moduleCallCommandFilters(client *c);
void moduleFireCommandResultEvent(client *c,
                                  struct serverCommand *cmd,
                                  int command_failed,
                                  long long duration,
                                  long long dirty);
void moduleFireCommandRejectedEvent(client *c, const char *reply_str);
void moduleFireCommandACLRejectedEvent(client *c, uint64_t subevent, int errpos);
void modulePostExecutionUnitOperations(void);
void ModuleForkDoneHandler(int exitcode, int bysignal);
int TerminateModuleForkChild(int child_pid, int wait);
ssize_t rdbSaveModulesAux(rio *rdb, int when);
int moduleAllDatatypesHandleErrors(void);
int moduleAllModulesHandleReplAsyncLoad(void);
int moduleVerifyAllAllowAtomicSlotMigrationOrReply(client *c);
sds modulesCollectInfo(sds info, dict *sections_dict, int for_crash_report, int sections);
void moduleFireServerEvent(uint64_t eid, int subid, void *data);
void processModuleLoadingProgressEvent(int is_aof);
int moduleTryServeClientBlockedOnKey(client *c, robj *key);
void moduleUnblockClient(client *c);
int moduleBlockedClientMayTimeout(client *c);
int moduleClientIsBlockedOnKeys(client *c);
void moduleNotifyUserChanged(client *c);
void moduleNotifyKeyUnlink(robj *key, robj *val, int dbid, int flags);
size_t moduleGetFreeEffort(robj *key, robj *val, int dbid);
size_t moduleGetMemUsage(robj *key, robj *val, size_t sample_size, int dbid);
robj *moduleTypeDupOrReply(client *c, robj *fromkey, robj *tokey, int todb, robj *value);
int moduleDefragValue(robj *key, robj *obj, int dbid);
int moduleLateDefrag(robj *key, robj *value, unsigned long *cursor, monotime endtime, int dbid);
void moduleDefragGlobals(void);
void *moduleGetHandleByName(char *modulename);
int moduleIsModuleCommand(void *module_handle, struct serverCommand *cmd);
void freeClientModuleData(client *c);
int checkModuleAuthentication(client *c, robj *username, robj *password, robj **err);
void moduleFireAuthenticationEvent(uint64_t client_id, const char *username, const char *module_name, int is_granted);

#endif /* _MODULE_H_ */
