// SPDX-FileCopyrightText: 2024 Redict Contributors
// SPDX-FileCopyrightText: 2024 Salvatore Sanfilippo <antirez at gmail dot com>
//
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-License-Identifier: LGPL-3.0-only

/* 
 * A module the tests RM_ReplyWith family of commands
 */

#include "redictmodule.h"
#include <math.h>

int rw_string(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 2) return RedictModule_WrongArity(ctx);

    return RedictModule_ReplyWithString(ctx, argv[1]);
}

int rw_cstring(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    REDICTMODULE_NOT_USED(argv);
    if (argc != 1) return RedictModule_WrongArity(ctx);

    return RedictModule_ReplyWithSimpleString(ctx, "A simple string");
}

int rw_int(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 2) return RedictModule_WrongArity(ctx);

    long long integer;
    if (RedictModule_StringToLongLong(argv[1], &integer) != REDICTMODULE_OK)
        return RedictModule_ReplyWithError(ctx, "Arg cannot be parsed as an integer");

    return RedictModule_ReplyWithLongLong(ctx, integer);
}

/* When one argument is given, it is returned as a double,
 * when two arguments are given, it returns a/b. */
int rw_double(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc==1)
        return RedictModule_ReplyWithDouble(ctx, NAN);

    if (argc != 2 && argc != 3) return RedictModule_WrongArity(ctx);

    double dbl, dbl2;
    if (RedictModule_StringToDouble(argv[1], &dbl) != REDICTMODULE_OK)
        return RedictModule_ReplyWithError(ctx, "Arg cannot be parsed as a double");
    if (argc == 3) {
        if (RedictModule_StringToDouble(argv[2], &dbl2) != REDICTMODULE_OK)
            return RedictModule_ReplyWithError(ctx, "Arg cannot be parsed as a double");
        dbl /= dbl2;
    }

    return RedictModule_ReplyWithDouble(ctx, dbl);
}

int rw_longdouble(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 2) return RedictModule_WrongArity(ctx);

    long double longdbl;
    if (RedictModule_StringToLongDouble(argv[1], &longdbl) != REDICTMODULE_OK)
        return RedictModule_ReplyWithError(ctx, "Arg cannot be parsed as a double");

    return RedictModule_ReplyWithLongDouble(ctx, longdbl);
}

int rw_bignumber(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 2) return RedictModule_WrongArity(ctx);

    size_t bignum_len;
    const char *bignum_str = RedictModule_StringPtrLen(argv[1], &bignum_len);

    return RedictModule_ReplyWithBigNumber(ctx, bignum_str, bignum_len);
}

int rw_array(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 2) return RedictModule_WrongArity(ctx);

    long long integer;
    if (RedictModule_StringToLongLong(argv[1], &integer) != REDICTMODULE_OK)
        return RedictModule_ReplyWithError(ctx, "Arg cannot be parsed as a integer");

    RedictModule_ReplyWithArray(ctx, integer);
    for (int i = 0; i < integer; ++i) {
        RedictModule_ReplyWithLongLong(ctx, i);
    }

    return REDICTMODULE_OK;
}

int rw_map(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 2) return RedictModule_WrongArity(ctx);

    long long integer;
    if (RedictModule_StringToLongLong(argv[1], &integer) != REDICTMODULE_OK)
        return RedictModule_ReplyWithError(ctx, "Arg cannot be parsed as a integer");

    RedictModule_ReplyWithMap(ctx, integer);
    for (int i = 0; i < integer; ++i) {
        RedictModule_ReplyWithLongLong(ctx, i);
        RedictModule_ReplyWithDouble(ctx, i * 1.5);
    }

    return REDICTMODULE_OK;
}

int rw_set(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 2) return RedictModule_WrongArity(ctx);

    long long integer;
    if (RedictModule_StringToLongLong(argv[1], &integer) != REDICTMODULE_OK)
        return RedictModule_ReplyWithError(ctx, "Arg cannot be parsed as a integer");

    RedictModule_ReplyWithSet(ctx, integer);
    for (int i = 0; i < integer; ++i) {
        RedictModule_ReplyWithLongLong(ctx, i);
    }

    return REDICTMODULE_OK;
}

int rw_attribute(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 2) return RedictModule_WrongArity(ctx);

    long long integer;
    if (RedictModule_StringToLongLong(argv[1], &integer) != REDICTMODULE_OK)
        return RedictModule_ReplyWithError(ctx, "Arg cannot be parsed as a integer");

    if (RedictModule_ReplyWithAttribute(ctx, integer) != REDICTMODULE_OK) {
        return RedictModule_ReplyWithError(ctx, "Attributes aren't supported by RESP 2");
    }

    for (int i = 0; i < integer; ++i) {
        RedictModule_ReplyWithLongLong(ctx, i);
        RedictModule_ReplyWithDouble(ctx, i * 1.5);
    }

    RedictModule_ReplyWithSimpleString(ctx, "OK");
    return REDICTMODULE_OK;
}

int rw_bool(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    REDICTMODULE_NOT_USED(argv);
    if (argc != 1) return RedictModule_WrongArity(ctx);

    RedictModule_ReplyWithArray(ctx, 2);
    RedictModule_ReplyWithBool(ctx, 0);
    return RedictModule_ReplyWithBool(ctx, 1);
}

int rw_null(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    REDICTMODULE_NOT_USED(argv);
    if (argc != 1) return RedictModule_WrongArity(ctx);

    return RedictModule_ReplyWithNull(ctx);
}

int rw_error(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    REDICTMODULE_NOT_USED(argv);
    if (argc != 1) return RedictModule_WrongArity(ctx);

    return RedictModule_ReplyWithError(ctx, "An error");
}

int rw_error_format(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 3) return RedictModule_WrongArity(ctx);

    return RedictModule_ReplyWithErrorFormat(ctx,
                                            RedictModule_StringPtrLen(argv[1], NULL),
                                            RedictModule_StringPtrLen(argv[2], NULL));
}

int rw_verbatim(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 2) return RedictModule_WrongArity(ctx);

    size_t verbatim_len;
    const char *verbatim_str = RedictModule_StringPtrLen(argv[1], &verbatim_len);

    return RedictModule_ReplyWithVerbatimString(ctx, verbatim_str, verbatim_len);
}

int RedictModule_OnLoad(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    REDICTMODULE_NOT_USED(argv);
    REDICTMODULE_NOT_USED(argc);
    if (RedictModule_Init(ctx, "replywith", 1, REDICTMODULE_APIVER_1) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"rw.string",rw_string,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;
    if (RedictModule_CreateCommand(ctx,"rw.cstring",rw_cstring,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;
    if (RedictModule_CreateCommand(ctx,"rw.bignumber",rw_bignumber,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;
    if (RedictModule_CreateCommand(ctx,"rw.int",rw_int,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;
    if (RedictModule_CreateCommand(ctx,"rw.double",rw_double,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;
    if (RedictModule_CreateCommand(ctx,"rw.longdouble",rw_longdouble,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;
    if (RedictModule_CreateCommand(ctx,"rw.array",rw_array,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;
    if (RedictModule_CreateCommand(ctx,"rw.map",rw_map,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;
    if (RedictModule_CreateCommand(ctx,"rw.attribute",rw_attribute,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;
    if (RedictModule_CreateCommand(ctx,"rw.set",rw_set,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;
    if (RedictModule_CreateCommand(ctx,"rw.bool",rw_bool,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;
    if (RedictModule_CreateCommand(ctx,"rw.null",rw_null,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;
    if (RedictModule_CreateCommand(ctx,"rw.error",rw_error,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;
    if (RedictModule_CreateCommand(ctx,"rw.error_format",rw_error_format,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;
    if (RedictModule_CreateCommand(ctx,"rw.verbatim",rw_verbatim,"",0,0,0) != REDICTMODULE_OK)
        return REDICTMODULE_ERR;

    return REDICTMODULE_OK;
}
