/**
 * @file value_bitwise.c
 * @brief Tests the bitwise operators and their type guards.
 */

#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;
}

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

    TEST_REQUIRE_OK(akbasic_error_register());

    set_int(&A, 0x0F);
    set_int(&B, 0x33);

    TEST_REQUIRE_OK(akbasic_value_bitwise_and(&A, &B, &SCRATCH, &out));
    TEST_REQUIRE_INT(out->intval, 0x03);
    TEST_REQUIRE_OK(akbasic_value_bitwise_or(&A, &B, &SCRATCH, &out));
    TEST_REQUIRE_INT(out->intval, 0x3F);
    TEST_REQUIRE_OK(akbasic_value_bitwise_xor(&A, &B, &SCRATCH, &out));
    TEST_REQUIRE_INT(out->intval, 0x3C);

    set_int(&A, 1);
    TEST_REQUIRE_OK(akbasic_value_shift_left(&A, 8, &SCRATCH, &out));
    TEST_REQUIRE_INT(out->intval, 256);
    set_int(&A, 256);
    TEST_REQUIRE_OK(akbasic_value_shift_right(&A, 8, &SCRATCH, &out));
    TEST_REQUIRE_INT(out->intval, 1);

    set_int(&A, 0);
    TEST_REQUIRE_OK(akbasic_value_bitwise_not(&A, &SCRATCH, &out));
    TEST_REQUIRE_INT(out->intval, -1);

    /*
     * Go's shift is defined for any count; C's is not. Refusing an out-of-range
     * count is a deliberate deviation from the reference, which would have
     * produced a defined (if useless) answer where C would produce UB.
     */
    set_int(&A, 1);
    TEST_REQUIRE_STATUS(akbasic_value_shift_left(&A, 64, &SCRATCH, &out), AKBASIC_ERR_VALUE);
    TEST_REQUIRE_STATUS(akbasic_value_shift_left(&A, -1, &SCRATCH, &out), AKBASIC_ERR_VALUE);
    TEST_REQUIRE_STATUS(akbasic_value_shift_right(&A, 64, &SCRATCH, &out), AKBASIC_ERR_VALUE);

    /* Bitwise operations are integers-only. */
    test_discard_error(akbasic_value_zero(&A));
    A.valuetype = AKBASIC_TYPE_FLOAT;
    A.floatval = 1.0;
    set_int(&B, 1);
    TEST_REQUIRE_STATUS(akbasic_value_bitwise_and(&A, &B, &SCRATCH, &out), AKBASIC_ERR_TYPE);
    TEST_REQUIRE_STATUS(akbasic_value_bitwise_or(&A, &B, &SCRATCH, &out), AKBASIC_ERR_TYPE);
    TEST_REQUIRE_STATUS(akbasic_value_bitwise_xor(&A, &B, &SCRATCH, &out), AKBASIC_ERR_TYPE);
    TEST_REQUIRE_STATUS(akbasic_value_bitwise_not(&A, &SCRATCH, &out), AKBASIC_ERR_TYPE);
    TEST_REQUIRE_STATUS(akbasic_value_shift_left(&A, 1, &SCRATCH, &out), AKBASIC_ERR_TYPE);

    return akbasic_test_failures;
}
