namespace PhpPwBytecode {
    [CCode (cname = "valaphp_pwphpbytecode_ensure_initialized", cheader_filename = "pwphpbytecode_runtime.h")]
    private extern static bool c_ensure_initialized (out string? error_message);

    [CCode (cname = "valaphp_pwphpbytecode_begin_load_class", cheader_filename = "pwphpbytecode_runtime.h")]
    private extern static bool c_begin_load_class (string loader_id, out bool should_load, out string? error_message);

    [CCode (cname = "valaphp_pwphpbytecode_finish_load_class", cheader_filename = "pwphpbytecode_runtime.h")]
    private extern static void c_finish_load_class (string loader_id);

    [CCode (cname = "valaphp_pwphpbytecode_call_static_method_zval", cheader_filename = "pwphpbytecode_runtime.h")]
    private extern static Php.zval? c_call_static_method_zval (string class_name, string method_name, Php.zval args, out string? error_message);

    [CCode (cname = "valaphp_pwphpbytecode_call_static_method_output_to_string", cheader_filename = "pwphpbytecode_runtime.h")]
    private extern static string? c_call_static_method_output_to_string (string class_name, string method_name, Php.zval args, out string? error_message);

    public bool ensure_initialized (out string? error_message) {
        return c_ensure_initialized (out error_message);
    }

    public void ensure_initialized_or_throw (string php_file, int php_line) throws Error {
        string? error_message = null;
        if (!c_ensure_initialized (out error_message)) {
            StacktraceError.throw (Php.with_php_source (error_message ?? "pwphpbytecode initialization failed", php_file, php_line));
        }
    }

    public bool begin_load_class (string loader_id, string php_file, int php_line) throws Error {
        string? error_message = null;
        bool should_load = false;
        if (!c_begin_load_class (loader_id, out should_load, out error_message)) {
            StacktraceError.throw (Php.with_php_source (error_message ?? "pwphpbytecode class loader failed", php_file, php_line));
        }
        return should_load;
    }

    public void finish_load_class (string loader_id) {
        c_finish_load_class (loader_id);
    }

    private Php.zval make_args (Php.zval[] values) {
        var args = Php.zval.array ();
        foreach (Php.zval value in values) {
            args.append (value);
        }
        return args;
    }

    public Php.zval call_static_method_zval (string class_name, string method_name, Php.zval args, string php_file, int php_line) throws Error {
        string? error_message = null;
        Php.zval? result = c_call_static_method_zval (class_name, method_name, args, out error_message);
        if (result == null) {
            StacktraceError.throw (Php.with_php_source (error_message ?? "pwphpbytecode %s::%s failed".printf (class_name, method_name), php_file, php_line));
        }
        return result;
    }

    public Php.zval call_static_method_zval_params (string class_name, string method_name, string php_file, int php_line, params Php.zval[] values) throws Error {
        return call_static_method_zval (class_name, method_name, make_args (values), php_file, php_line);
    }

    public string call_static_method_output_to_string (string class_name, string method_name, Php.zval args, string php_file, int php_line) throws Error {
        string? error_message = null;
        string? result = c_call_static_method_output_to_string (class_name, method_name, args, out error_message);
        if (result == null) {
            StacktraceError.throw (Php.with_php_source (error_message ?? "pwphpbytecode %s::%s failed".printf (class_name, method_name), php_file, php_line));
        }
        return result;
    }

    public void call_static_method_void (string class_name, string method_name, string php_file, int php_line, params Php.zval[] values) throws Error {
        call_static_method_zval (class_name, method_name, make_args (values), php_file, php_line);
    }

    public int call_static_method_int (string class_name, string method_name, string php_file, int php_line, params Php.zval[] values) throws Error {
        return (int) Php.zval_to_int (call_static_method_zval (class_name, method_name, make_args (values), php_file, php_line));
    }

    public string call_static_method_string (string class_name, string method_name, string php_file, int php_line, params Php.zval[] values) throws Error {
        return Php.zval_to_string (call_static_method_zval (class_name, method_name, make_args (values), php_file, php_line));
    }
}
