
PhpefRuntimeCore.Zval smoke_user_function(PhpefRuntimeCore.Zval[] args) throws Error {
    assert(args.length == 1);
    return PhpefRuntimeCore.Zval.from_string("user:" + args[0].to_php_string());
}

PhpefRuntimeCore.Zval smoke_static_method(PhpefRuntimeCore.Zval[] args) throws Error {
    assert(args.length == 0);
    return PhpefRuntimeCore.Zval.from_string("method");
}

PhpefRuntimeCore.Zval smoke_private_static_method(PhpefRuntimeCore.Zval[] args) throws Error {
    assert(args.length == 0);
    return PhpefRuntimeCore.Zval.from_string("private-method");
}

int main(string[] args) {
    var n = PhpefRuntimeCore.Zval.from_null();
    assert(n.kind == PhpefRuntimeCore.ValueKind.NULL);
    assert(n.to_php_string() == "");
    assert(!n.to_bool());

    var u = PhpefRuntimeCore.Zval.undefined();
    assert(u.kind == PhpefRuntimeCore.ValueKind.UNDEF);
    assert(u.is_undefined());
    assert(!u.to_bool());

    var b = PhpefRuntimeCore.Zval.from_bool(true);
    assert(b.kind == PhpefRuntimeCore.ValueKind.BOOL);
    assert(b.as_bool());
    assert(b.to_php_string() == "1");
    assert(b.to_bool());

    var i = PhpefRuntimeCore.Zval.from_int(42);
    assert(i.kind == PhpefRuntimeCore.ValueKind.INT);
    assert(i.as_int() == 42);
    assert(i.to_php_string() == "42");
    assert(i.to_bool());
    assert(!PhpefRuntimeCore.Zval.from_int(0).to_bool());

    var s = PhpefRuntimeCore.Zval.from_string("5");
    assert(s.kind == PhpefRuntimeCore.ValueKind.STRING);
    assert(s.as_string() == "5");
    assert(s.to_bool());
    assert(!PhpefRuntimeCore.Zval.from_string("").to_bool());
    assert(!PhpefRuntimeCore.Zval.from_string("0").to_bool());

    var resource = PhpefRuntimeCore.Zval.from_resource_pointer((void*) 0x1);
    assert(resource.to_bool());
    assert(!resource.equal(PhpefRuntimeCore.Zval.from_bool(false)).to_bool());
    assert(resource.not_equal(PhpefRuntimeCore.Zval.from_bool(false)).to_bool());

    var sum = s.add(PhpefRuntimeCore.Zval.from_int(4));
    assert(sum.kind == PhpefRuntimeCore.ValueKind.INT);
    assert(sum.to_php_string() == "9");

    assert(PhpefRuntimeCore.Zval.from_int(4).greater(PhpefRuntimeCore.Zval.from_int(3)).to_bool());
    assert(!PhpefRuntimeCore.Zval.from_int(3).greater(PhpefRuntimeCore.Zval.from_int(3)).to_bool());
    assert(PhpefRuntimeCore.Zval.from_int(5).not_identical(PhpefRuntimeCore.Zval.from_string("5")).to_bool());
    assert(!PhpefRuntimeCore.Zval.from_string("5").not_identical(PhpefRuntimeCore.Zval.from_string("5")).to_bool());
    assert(PhpefRuntimeCore.Zval.from_int(1).smaller(PhpefRuntimeCore.Zval.from_int(2)).to_bool());
    assert(!PhpefRuntimeCore.Zval.from_int(2).smaller(PhpefRuntimeCore.Zval.from_int(2)).to_bool());
    assert(PhpefRuntimeCore.Zval.from_int(1).smaller_or_equal(PhpefRuntimeCore.Zval.from_int(2)).to_bool());
    assert(PhpefRuntimeCore.Zval.from_int(2).smaller_or_equal(PhpefRuntimeCore.Zval.from_int(2)).to_bool());
    assert(!PhpefRuntimeCore.Zval.from_int(3).smaller_or_equal(PhpefRuntimeCore.Zval.from_int(2)).to_bool());
    assert(PhpefRuntimeCore.Zval.from_int(7).subtract(PhpefRuntimeCore.Zval.from_int(3)).to_php_string() == "4");
    assert(PhpefRuntimeCore.Zval.from_int(6).multiply(PhpefRuntimeCore.Zval.from_int(7)).to_php_string() == "42");

    var concat = PhpefRuntimeCore.Zval.from_string("Welt").concat(PhpefRuntimeCore.Zval.from_int(1));
    assert(concat.kind == PhpefRuntimeCore.ValueKind.STRING);
    assert(concat.to_php_string() == "Welt1");

    uint8[] binary_bytes = { 'a', 0, 'b' };
    var binary_string = PhpefRuntimeCore.Zval.from_string_bytes(binary_bytes);
    assert(binary_string.kind == PhpefRuntimeCore.ValueKind.STRING);
    assert(binary_string.to_php_string() == "a");
    assert(binary_string.to_php_bytes().length == 3);
    assert(binary_string.to_php_bytes()[0] == 'a');
    assert(binary_string.to_php_bytes()[1] == 0);
    assert(binary_string.to_php_bytes()[2] == 'b');

    var binary_concat = PhpefRuntimeCore.Zval.from_string("x").concat(binary_string);
    assert(binary_concat.to_php_bytes().length == 4);
    assert(binary_concat.to_php_bytes()[0] == 'x');
    assert(binary_concat.to_php_bytes()[2] == 0);
    assert(binary_concat.to_php_bytes()[3] == 'b');
    assert(binary_string.identical(PhpefRuntimeCore.Zval.from_string_bytes(binary_bytes)).to_bool());
    assert(binary_string.not_identical(PhpefRuntimeCore.Zval.from_string("a")).to_bool());

    PhpefRuntimeCore.register_function("Smoke_User", smoke_user_function, "phpef_tests_smoke__Smoke_User", 1, 1);
    assert(PhpefRuntimeCore.has_registered_function("smoke_user"));
    var metadata = PhpefRuntimeCore.lookup_registered_function("SMOKE_USER");
    assert(metadata != null);
    assert(metadata.php_name == "Smoke_User");
    assert(metadata.handler_symbol == "phpef_tests_smoke__Smoke_User");
    assert(metadata.min_args == 1);
    assert(metadata.max_args == 1);
    assert(PhpefRuntimeCore.lookup_registered_function_by_handler("phpef_tests_smoke__Smoke_User").php_name == "Smoke_User");
    var called = PhpefRuntimeCore.call_function("smoke_user", new PhpefRuntimeCore.Zval[] { PhpefRuntimeCore.Zval.from_string("ok") });
    assert(called.to_php_string() == "user:ok");

    PhpefRuntimeCore.register_class("Smoke_Class");
    assert(PhpefRuntimeCore.has_registered_class("smoke_class"));
    var class_metadata = PhpefRuntimeCore.lookup_registered_class("SMOKE_CLASS");
    assert(class_metadata != null);
    assert(class_metadata.php_name == "Smoke_Class");
    PhpefRuntimeCore.register_static_method("smoke_class", "Smoke_Method", smoke_static_method, "phpef_tests_smoke__Smoke_Class__Smoke_Method", 0, 0);
    assert(PhpefRuntimeCore.has_registered_static_method("SMOKE_CLASS", "smoke_method"));
    var method_metadata = PhpefRuntimeCore.lookup_registered_static_method("Smoke_Class", "SMOKE_METHOD");
    assert(method_metadata != null);
    assert(method_metadata.php_name == "Smoke_Class::Smoke_Method");
    assert(method_metadata.handler_symbol == "phpef_tests_smoke__Smoke_Class__Smoke_Method");
    assert(PhpefRuntimeCore.lookup_registered_static_method_by_handler("phpef_tests_smoke__Smoke_Class__Smoke_Method").php_name == "Smoke_Class::Smoke_Method");
    assert(method_metadata.visibility == PhpefRuntimeCore.Visibility.PUBLIC);
    var method_called = PhpefRuntimeCore.call_static_method("SMOKE_CLASS", "smoke_method", new PhpefRuntimeCore.Zval[] {});
    assert(method_called.to_php_string() == "method");

    PhpefRuntimeCore.register_class("Smoke_Private_Class");
    PhpefRuntimeCore.register_static_method("smoke_private_class", "Secret", smoke_private_static_method, "phpef_tests_smoke__Smoke_Private_Class__Secret", 0, 0, PhpefRuntimeCore.Visibility.PRIVATE);
    var private_metadata = PhpefRuntimeCore.lookup_registered_static_method("Smoke_Private_Class", "secret");
    assert(private_metadata != null);
    assert(private_metadata.visibility == PhpefRuntimeCore.Visibility.PRIVATE);
    var private_called = PhpefRuntimeCore.call_static_method("Smoke_Private_Class", "Secret", new PhpefRuntimeCore.Zval[] {}, "Smoke_Private_Class");
    assert(private_called.to_php_string() == "private-method");
    bool private_blocked = false;
    try {
        PhpefRuntimeCore.call_static_method("Smoke_Private_Class", "Secret", new PhpefRuntimeCore.Zval[] {});
    } catch (Error e) {
        private_blocked = e.message.contains("Call to private method Smoke_Private_Class::Secret() from global scope");
    }
    assert(private_blocked);

    bool throwable_caught = false;
    try {
        PhpefRuntimeCore.throw_exception("RuntimeException", PhpefRuntimeCore.Zval.from_string("boom"), PhpefRuntimeCore.Zval.from_int(23));
    } catch (Error e) {
        throwable_caught = true;
        assert(PhpefRuntimeCore.exception_is_a(e, "RuntimeException"));
        assert(PhpefRuntimeCore.exception_is_a(e, "Exception"));
        assert(PhpefRuntimeCore.exception_is_a(e, "Throwable"));
        assert(!PhpefRuntimeCore.exception_is_a(e, "Error"));
        assert(PhpefRuntimeCore.exception_get_message(e).to_php_string() == "boom");
        assert(PhpefRuntimeCore.exception_get_code(e).as_int() == 23);
        var throwable = PhpefRuntimeCore.exception_get_throwable(e);
        assert(throwable != null);
        assert(throwable.kind == PhpefRuntimeCore.ValueKind.OBJECT);
        assert(throwable.to_php_string() == "(OBJECT unsupported currently)");
        var object_payload = throwable.as_object() as PhpefRuntimeCore.PhpThrowableObject;
        assert(object_payload != null);
        assert(object_payload.php_class_name() == "RuntimeException");
        assert(object_payload.get_php_property("message").to_php_string() == "boom");
    }
    assert(throwable_caught);

    stdout.printf("core_smoke=ok\n");

    var array_value = PhpefRuntimeCore.Zval.from_array(new PhpefRuntimeCore.ZvalArrayEntry[] {
        new PhpefRuntimeCore.ZvalArrayEntry(null, PhpefRuntimeCore.Zval.from_string("a")),
        new PhpefRuntimeCore.ZvalArrayEntry(null, PhpefRuntimeCore.Zval.from_string("b"))
    });
    array_value.array_append(PhpefRuntimeCore.Zval.from_string("c"));
    var entries = array_value.as_array_entries();
    if (array_value.kind != PhpefRuntimeCore.ValueKind.ARRAY || entries.length != 3 || entries[2].key.as_int() != 2 || entries[2].value.to_php_string() != "c") {
        stderr.printf("array Zval smoke failed\\n");
        return 1;
    }
    array_value.array_unset_key(PhpefRuntimeCore.Zval.from_int(2));
    array_value.array_append(PhpefRuntimeCore.Zval.from_string("d"));
    entries = array_value.as_array_entries();
    if (entries.length != 3 || entries[2].key.as_int() != 3 || entries[2].value.to_php_string() != "d") {
        stderr.printf("array unset Zval smoke failed\\n");
        return 1;
    }

    var array_union_left = PhpefRuntimeCore.Zval.from_array(new PhpefRuntimeCore.ZvalArrayEntry[] { new PhpefRuntimeCore.ZvalArrayEntry(PhpefRuntimeCore.Zval.from_string("address"), PhpefRuntimeCore.Zval.from_int(1)), new PhpefRuntimeCore.ZvalArrayEntry(PhpefRuntimeCore.Zval.from_int(0), PhpefRuntimeCore.Zval.from_string("left-zero")) });
    var array_union_right = PhpefRuntimeCore.Zval.from_array(new PhpefRuntimeCore.ZvalArrayEntry[] { new PhpefRuntimeCore.ZvalArrayEntry(PhpefRuntimeCore.Zval.from_string("address"), PhpefRuntimeCore.Zval.from_int(99)), new PhpefRuntimeCore.ZvalArrayEntry(PhpefRuntimeCore.Zval.from_string("voltage_mv"), PhpefRuntimeCore.Zval.from_int(33000)), new PhpefRuntimeCore.ZvalArrayEntry(PhpefRuntimeCore.Zval.from_string("0"), PhpefRuntimeCore.Zval.from_string("right-zero")) });
    var array_union = array_union_left.add(array_union_right);
    entries = array_union.as_array_entries();
    if (array_union.kind != PhpefRuntimeCore.ValueKind.ARRAY || entries.length != 3 || array_union.array_get_key(PhpefRuntimeCore.Zval.from_string("address")).as_int() != 1 || array_union.array_get_key(PhpefRuntimeCore.Zval.from_string("voltage_mv")).as_int() != 33000 || array_union.array_get_key(PhpefRuntimeCore.Zval.from_int(0)).to_php_string() != "left-zero") { stderr.printf("array union Zval add smoke failed\n"); return 1; }

    return 0;
}
