/**
 * @file value_compare.c
 * @brief Tests the comparison operators across integer, float and string.
 */

#include <string.h>

#include <akbasic/error.h>
#include <akbasic/value.h>

#include "testutil.h"

static akbasic_Value A;
static akbasic_Value B;
static akbasic_Value SCRATCH;

static void set_int(akbasic_Value *v, int64_t n)
{
    test_discard_error(akbasic_value_zero(v));
    v->valuetype = AKBASIC_TYPE_INTEGER;
    v->intval = n;
}

static void set_float(akbasic_Value *v, double n)
{
    test_discard_error(akbasic_value_zero(v));
    v->valuetype = AKBASIC_TYPE_FLOAT;
    v->floatval = n;
}

static void set_string(akbasic_Value *v, const char *s)
{
    test_discard_error(akbasic_value_zero(v));
    v->valuetype = AKBASIC_TYPE_STRING;
    strncpy(v->stringval, s, AKBASIC_MAX_STRING_LENGTH - 1);
}

#define EXPECT_BOOL(__call, __want)					\
    do {								\
	akbasic_Value *__out = NULL;					\
	TEST_REQUIRE_OK(__call(&A, &B, &SCRATCH, &__out));		\
	TEST_REQUIRE(__out->valuetype == AKBASIC_TYPE_BOOLEAN,		\
		     #__call " did not produce a boolean");		\
	TEST_REQUIRE(akbasic_value_is_true(__out) == (__want),		\
		     #__call " expected %s", ((__want) ? "true" : "false")); \
	TEST_REQUIRE_INT(__out->boolvalue, ((__want) ? AKBASIC_TRUE : AKBASIC_FALSE)); \
    } while ( 0 )

int main(void)
{
    akbasic_Value *out = NULL;

    TEST_REQUIRE_OK(akbasic_error_register());

    /* Integers. */
    set_int(&A, 3);
    set_int(&B, 5);
    EXPECT_BOOL(akbasic_value_less_than, true);
    EXPECT_BOOL(akbasic_value_less_than_equal, true);
    EXPECT_BOOL(akbasic_value_greater_than, false);
    EXPECT_BOOL(akbasic_value_greater_than_equal, false);
    EXPECT_BOOL(akbasic_value_is_equal, false);
    EXPECT_BOOL(akbasic_value_is_not_equal, true);

    set_int(&B, 3);
    EXPECT_BOOL(akbasic_value_less_than, false);
    EXPECT_BOOL(akbasic_value_less_than_equal, true);
    EXPECT_BOOL(akbasic_value_greater_than_equal, true);
    EXPECT_BOOL(akbasic_value_is_equal, true);

    /* Floats. */
    set_float(&A, 1.5);
    set_float(&B, 2.5);
    EXPECT_BOOL(akbasic_value_less_than, true);
    EXPECT_BOOL(akbasic_value_greater_than, false);

    /* Strings compare lexically. */
    set_string(&A, "APPLE");
    set_string(&B, "BANANA");
    EXPECT_BOOL(akbasic_value_less_than, true);
    EXPECT_BOOL(akbasic_value_greater_than, false);
    EXPECT_BOOL(akbasic_value_is_equal, false);
    set_string(&B, "APPLE");
    EXPECT_BOOL(akbasic_value_is_equal, true);
    EXPECT_BOOL(akbasic_value_less_than_equal, true);
    EXPECT_BOOL(akbasic_value_greater_than_equal, true);

    /* BASIC true is -1, not 1: the Commodore convention the README calls out. */
    set_int(&A, 1);
    set_int(&B, 1);
    TEST_REQUIRE_OK(akbasic_value_is_equal(&A, &B, &SCRATCH, &out));
    TEST_REQUIRE_INT(out->boolvalue, -1);

    /* is_true is false for anything that is not a boolean. */
    set_int(&A, -1);
    TEST_REQUIRE(!akbasic_value_is_true(&A), "an integer -1 is not a BASIC true");
    TEST_REQUIRE(!akbasic_value_is_true(NULL), "NULL is not true");

    TEST_REQUIRE_STATUS(akbasic_value_is_equal(&A, NULL, &SCRATCH, &out), AKERR_NULLPOINTER);

    return akbasic_test_failures;
}
