
/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) NGINX, Inc.
 */

#ifndef _NJS_VALUE_CONVERSION_H_INCLUDED_
#define _NJS_VALUE_CONVERSION_H_INCLUDED_


njs_inline njs_int_t
njs_value_to_number(njs_vm_t *vm, njs_value_t *value, double *dst)
{
    njs_int_t    ret;
    njs_value_t  primitive;

    if (njs_slow_path(!njs_is_primitive(value))) {
        ret = njs_value_to_primitive(vm, &primitive, value, 0);
        if (njs_slow_path(ret != NJS_OK)) {
            return ret;
        }

        value = &primitive;
    }

    if (njs_slow_path(!njs_is_numeric(value))) {

        if (njs_slow_path(njs_is_symbol(value))) {
            njs_symbol_conversion_failed(vm, 0);
            return NJS_ERROR;
        }

        *dst = NAN;

        if (njs_is_string(value)) {
            *dst = njs_string_to_number(value, 0);
        }

        return NJS_OK;
    }

    *dst = njs_number(value);

    return NJS_OK;
}


njs_inline njs_int_t
njs_value_to_numeric(njs_vm_t *vm, njs_value_t *value, njs_value_t *dst)
{
    double     num;
    njs_int_t  ret;

    ret = njs_value_to_number(vm, value, &num);
    if (njs_slow_path(ret != NJS_OK)) {
        return ret;
    }

    njs_set_number(dst, num);

    return NJS_OK;
}


njs_inline njs_int_t
njs_value_to_integer(njs_vm_t *vm, njs_value_t *value, int64_t *dst)
{
    double     num;
    njs_int_t  ret;

    ret = njs_value_to_number(vm, value, &num);
    if (njs_slow_path(ret != NJS_OK)) {
        return ret;
    }

    *dst = njs_number_to_integer(num);

    return NJS_OK;
}


njs_inline njs_int_t
njs_value_to_length(njs_vm_t *vm, njs_value_t *value, int64_t *dst)
{
    double     num;
    njs_int_t  ret;

    ret = njs_value_to_number(vm, value, &num);
    if (njs_slow_path(ret != NJS_OK)) {
        return ret;
    }

    *dst = njs_number_to_length(num);

    return NJS_OK;
}


njs_inline njs_int_t
njs_value_to_index(njs_vm_t *vm, njs_value_t *value, uint64_t *dst)
{
    int64_t    integer_index;
    njs_int_t  ret;

    if (njs_slow_path(njs_is_undefined(value))) {
        *dst = 0;

    } else {
        ret = njs_value_to_integer(vm, value, &integer_index);
        if (njs_slow_path(ret != NJS_OK)) {
            return ret;
        }

        if (integer_index < 0 || integer_index > UINT32_MAX) {
            njs_range_error(vm, "invalid index");
            return NJS_ERROR;
        }

        *dst = integer_index;
    }

    return NJS_OK;
}


njs_inline njs_int_t
njs_value_to_int32(njs_vm_t *vm, njs_value_t *value, int32_t *dst)
{
    double     num;
    njs_int_t  ret;

    ret = njs_value_to_number(vm, value, &num);
    if (njs_slow_path(ret != NJS_OK)) {
        return ret;
    }

    *dst = njs_number_to_int32(num);

    return NJS_OK;
}


njs_inline njs_int_t
njs_value_to_uint32(njs_vm_t *vm, njs_value_t *value, uint32_t *dst)
{
    double     num;
    njs_int_t  ret;

    ret = njs_value_to_number(vm, value, &num);
    if (njs_slow_path(ret != NJS_OK)) {
        return ret;
    }

    *dst = njs_number_to_uint32(num);

    return NJS_OK;
}


njs_inline njs_int_t
njs_value_to_uint16(njs_vm_t *vm, njs_value_t *value, uint16_t *dst)
{
    double     num;
    njs_int_t  ret;

    ret = njs_value_to_number(vm, value, &num);
    if (njs_slow_path(ret != NJS_OK)) {
        return ret;
    }

    *dst = njs_number_to_uint16(num);

    return NJS_OK;
}


njs_inline njs_int_t
njs_value_to_string(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value)
{
    njs_int_t    ret;
    njs_value_t  primitive;

    if (njs_slow_path(!njs_is_primitive(value))) {
        if (njs_slow_path(value->type == NJS_OBJECT_SYMBOL)) {
            /* should fail */
            value = njs_object_value(value);

        } else {
            ret = njs_value_to_primitive(vm, &primitive, value, 1);
            if (njs_slow_path(ret != NJS_OK)) {
                return ret;
            }

            value = &primitive;
        }
    }

    return njs_primitive_value_to_string(vm, dst, value);
}


/*
 * retval >= 0 is length (UTF8 characters) value of appended string.
 */
njs_inline njs_int_t
njs_value_to_chain(njs_vm_t *vm, njs_chb_t *chain, njs_value_t *value)
{
    njs_int_t    ret;
    njs_value_t  primitive;

    if (njs_slow_path(!njs_is_primitive(value))) {
        if (njs_slow_path(value->type == NJS_OBJECT_SYMBOL)) {
            /* should fail */
            value = njs_object_value(value);

        } else {
            ret = njs_value_to_primitive(vm, &primitive, value, 1);
            if (njs_slow_path(ret != NJS_OK)) {
                return ret;
            }

            value = &primitive;
        }
    }

    return njs_primitive_value_to_chain(vm, chain, value);
}


#endif /* _NJS_VALUE_CONVERSION_H_INCLUDED_ */
