using PhpefRuntimeCore;

static int failures = 0;

static bool near(double left, double right, double tolerance = 0.000001) {
    double delta = left - right;
    if (delta < 0.0) {
        delta = -delta;
    }
    return delta <= tolerance;
}

static void compare_zval(string label, Zval native_result, Zval embed_result) {
    if (native_result.kind != embed_result.kind) {
        stderr.printf("%s kind mismatch: native=%d embed=%d native_text=%s embed_text=%s\n",
            label, (int) native_result.kind, (int) embed_result.kind, native_result.to_php_string(), embed_result.to_php_string());
        failures++;
        return;
    }

    if (native_result.kind == ValueKind.FLOAT) {
        if (!near(native_result.as_float(), embed_result.as_float())) {
            stderr.printf("%s float mismatch: native=%.12f embed=%.12f\n", label, native_result.as_float(), embed_result.as_float());
            failures++;
        }
        return;
    }

    if (native_result.to_php_string() != embed_result.to_php_string()) {
        stderr.printf("%s value mismatch: native='%s' embed='%s'\n", label, native_result.to_php_string(), embed_result.to_php_string());
        failures++;
    }
}

static void compare_floatval(string label, Zval value) {
    compare_zval(label, PhpefRuntimeNativeStandard.phpfunc_floatval(value), PhpefRuntimeEmbed.phpfunc_floatval(value));
}

static void compare_floor(string label, Zval value) {
    compare_zval(label, PhpefRuntimeNativeStandard.phpfunc_floor(value), PhpefRuntimeEmbed.phpfunc_floor(value));
}

static void compare_round(string label, Zval value, Zval? precision = null) {
    Zval native_result = precision == null
        ? PhpefRuntimeNativeStandard.phpfunc_round(value)
        : PhpefRuntimeNativeStandard.phpfunc_round(value, precision);
    Zval embed_result = precision == null
        ? PhpefRuntimeEmbed.phpfunc_round(value)
        : PhpefRuntimeEmbed.phpfunc_round(value, precision);
    compare_zval(label, native_result, embed_result);
}

static void compare_number_format(string label, Zval value, Zval? decimals = null, Zval? decimal_separator = null, Zval? thousands_separator = null) {
    Zval native_result;
    Zval embed_result;
    if (decimals == null) {
        native_result = PhpefRuntimeNativeStandard.phpfunc_number_format(value);
        embed_result = PhpefRuntimeEmbed.phpfunc_number_format(value);
    } else if (decimal_separator == null) {
        native_result = PhpefRuntimeNativeStandard.phpfunc_number_format(value, decimals);
        embed_result = PhpefRuntimeEmbed.phpfunc_number_format(value, decimals);
    } else {
        native_result = PhpefRuntimeNativeStandard.phpfunc_number_format(value, decimals, decimal_separator, thousands_separator);
        embed_result = PhpefRuntimeEmbed.phpfunc_number_format(value, decimals, decimal_separator, thousands_separator);
    }
    compare_zval(label, native_result, embed_result);
}

public static int main(string[] args) {
    PhpefRuntimeEmbed.engine_start(args);

    compare_floatval("floatval int", Zval.from_int(42));
    compare_floatval("floatval numeric string", Zval.from_string("12.5"));
    compare_floatval("floatval bool true", Zval.from_bool(true));
    compare_floatval("floatval null", Zval.from_null());

    compare_floor("floor positive float", Zval.from_float(12.9));
    compare_floor("floor negative float", Zval.from_float(-12.1));
    compare_floor("floor numeric string", Zval.from_string("99.8"));

    compare_round("round positive half", Zval.from_float(12.5));
    compare_round("round negative half", Zval.from_float(-12.5));
    compare_round("round precision", Zval.from_float(12.345), Zval.from_int(2));
    compare_round("round negative precision clamped", Zval.from_float(1234.56), Zval.from_int(-1));

    compare_number_format("number_format default", Zval.from_int(1234567));
    compare_number_format("number_format decimals", Zval.from_float(1234.567), Zval.from_int(2));
    compare_number_format("number_format separators", Zval.from_float(1234.5), Zval.from_int(1), Zval.from_string(","), Zval.from_string("."));
    compare_number_format("number_format negative", Zval.from_float(-1234.5), Zval.from_int(1), Zval.from_string(","), Zval.from_string("."));

    PhpefRuntimeEmbed.engine_stop();
    return failures == 0 ? 0 : 1;
}
